/*!
 * OverlayScrollbars
 * https://github.com/KingSora/OverlayScrollbars
 *
 * Version: 1.13.3
 *
 * Copyright KingSora | Rene Haas.
 * https://github.com/KingSora
 *
 * Released under the MIT license.
 * Date: 20.07.2022
 */

(function (global, factory) {
  if (typeof define === "function" && define.amd)
    define(function () {
      return factory(global, global.document, undefined);
    });
  else if (typeof module === "object" && typeof module.exports === "object")
    module.exports = factory(global, global.document, undefined);
  else factory(global, global.document, undefined);
})(
  typeof window !== "undefined" ? window : this,
  function (window, document, undefined) {
    "use strict";
    var PLUGINNAME = "OverlayScrollbars";
    var TYPES = {
      o: "object",
      f: "function",
      a: "array",
      s: "string",
      b: "boolean",
      n: "number",
      u: "undefined",
      z: "null",
      //d : 'date',
      //e : 'error',
      //r : 'regexp',
      //y : 'symbol'
    };
    var LEXICON = {
      c: "class",
      s: "style",
      i: "id",
      l: "length",
      p: "prototype",
      ti: "tabindex",
      oH: "offsetHeight",
      cH: "clientHeight",
      sH: "scrollHeight",
      oW: "offsetWidth",
      cW: "clientWidth",
      sW: "scrollWidth",
      hOP: "hasOwnProperty",
      bCR: "getBoundingClientRect",
    };
    var VENDORS = (function () {
      //https://developer.mozilla.org/en-US/docs/Glossary/Vendor_Prefix
      var jsCache = {};
      var cssCache = {};
      var cssPrefixes = ["-webkit-", "-moz-", "-o-", "-ms-"];
      var jsPrefixes = ["WebKit", "Moz", "O", "MS"];
      function firstLetterToUpper(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
      }

      return {
        _cssPrefixes: cssPrefixes,
        _jsPrefixes: jsPrefixes,
        _cssProperty: function (name) {
          var result = cssCache[name];

          if (cssCache[LEXICON.hOP](name)) return result;

          var uppercasedName = firstLetterToUpper(name);
          var elmStyle = document.createElement("div")[LEXICON.s];
          var resultPossibilities;
          var i = 0;
          var v;
          var currVendorWithoutDashes;

          for (; i < cssPrefixes.length; i++) {
            currVendorWithoutDashes = cssPrefixes[i].replace(/-/g, "");
            resultPossibilities = [
              name, //transition
              cssPrefixes[i] + name, //-webkit-transition
              currVendorWithoutDashes + uppercasedName, //webkitTransition
              firstLetterToUpper(currVendorWithoutDashes) + uppercasedName, //WebkitTransition
            ];
            for (v = 0; v < resultPossibilities[LEXICON.l]; v++) {
              if (elmStyle[resultPossibilities[v]] !== undefined) {
                result = resultPossibilities[v];
                break;
              }
            }
          }

          cssCache[name] = result;
          return result;
        },
        _cssPropertyValue: function (property, values, suffix) {
          var name = property + " " + values;
          var result = cssCache[name];

          if (cssCache[LEXICON.hOP](name)) return result;

          var dummyStyle = document.createElement("div")[LEXICON.s];
          var possbleValues = values.split(" ");
          var preparedSuffix = suffix || "";
          var i = 0;
          var v = -1;
          var prop;

          for (; i < possbleValues[LEXICON.l]; i++) {
            for (; v < VENDORS._cssPrefixes[LEXICON.l]; v++) {
              prop =
                v < 0
                  ? possbleValues[i]
                  : VENDORS._cssPrefixes[v] + possbleValues[i];
              dummyStyle.cssText = property + ":" + prop + preparedSuffix;
              if (dummyStyle[LEXICON.l]) {
                result = prop;
                break;
              }
            }
          }

          cssCache[name] = result;
          return result;
        },
        _jsAPI: function (name, isInterface, fallback) {
          var i = 0;
          var result = jsCache[name];

          if (!jsCache[LEXICON.hOP](name)) {
            result = window[name];
            for (; i < jsPrefixes[LEXICON.l]; i++)
              result =
                result ||
                window[
                  (isInterface ? jsPrefixes[i] : jsPrefixes[i].toLowerCase()) +
                    firstLetterToUpper(name)
                ];
            jsCache[name] = result;
          }
          return result || fallback;
        },
      };
    })();
    var COMPATIBILITY = (function () {
      function windowSize(x) {
        return x
          ? window.innerWidth ||
              document.documentElement[LEXICON.cW] ||
              document.body[LEXICON.cW]
          : window.innerHeight ||
              document.documentElement[LEXICON.cH] ||
              document.body[LEXICON.cH];
      }
      function bind(func, thisObj) {
        if (typeof func != TYPES.f) {
          throw "Can't bind function!";
          // closest thing possible to the ECMAScript 5
          // internal IsCallable function
          //throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
        }
        var proto = LEXICON.p;
        var aArgs = Array[proto].slice.call(arguments, 2);
        var fNOP = function () {};
        var fBound = function () {
          return func.apply(
            this instanceof fNOP ? this : thisObj,
            aArgs.concat(Array[proto].slice.call(arguments))
          );
        };

        if (func[proto]) fNOP[proto] = func[proto]; // Function.prototype doesn't have a prototype property
        fBound[proto] = new fNOP();

        return fBound;
      }

      return {
        /**
         * Gets the current window width.
         * @returns {Number|number} The current window width in pixel.
         */
        wW: bind(windowSize, 0, true),

        /**
         * Gets the current window height.
         * @returns {Number|number} The current window height in pixel.
         */
        wH: bind(windowSize, 0),

        /**
         * Gets the MutationObserver Object or undefined if not supported.
         * @returns {MutationObserver|*|undefined} The MutationsObserver Object or undefined.
         */
        mO: bind(VENDORS._jsAPI, 0, "MutationObserver", true),

        /**
         * Gets the ResizeObserver Object or undefined if not supported.
         * @returns {MutationObserver|*|undefined} The ResizeObserver Object or undefined.
         */
        rO: bind(VENDORS._jsAPI, 0, "ResizeObserver", true),

        /**
         * Gets the RequestAnimationFrame method or it's corresponding polyfill.
         * @returns {*|Function} The RequestAnimationFrame method or it's corresponding polyfill.
         */
        rAF: bind(
          VENDORS._jsAPI,
          0,
          "requestAnimationFrame",
          false,
          function (func) {
            return window.setTimeout(func, 1000 / 60);
          }
        ),

        /**
         * Gets the CancelAnimationFrame method or it's corresponding polyfill.
         * @returns {*|Function} The CancelAnimationFrame method or it's corresponding polyfill.
         */
        cAF: bind(
          VENDORS._jsAPI,
          0,
          "cancelAnimationFrame",
          false,
          function (id) {
            return window.clearTimeout(id);
          }
        ),

        /**
         * Gets the current time.
         * @returns {number} The current time.
         */
        now: function () {
          return (Date.now && Date.now()) || new Date().getTime();
        },

        /**
         * Stops the propagation of the given event.
         * @param event The event of which the propagation shall be stoped.
         */
        stpP: function (event) {
          if (event.stopPropagation) event.stopPropagation();
          else event.cancelBubble = true;
        },

        /**
         * Prevents the default action of the given event.
         * @param event The event of which the default action shall be prevented.
         */
        prvD: function (event) {
          if (event.preventDefault && event.cancelable) event.preventDefault();
          else event.returnValue = false;
        },

        /**
         * Gets the pageX and pageY values of the given mouse event.
         * @param event The mouse event of which the pageX and pageX shall be got.
         * @returns {{x: number, y: number}} x = pageX value, y = pageY value.
         */
        page: function (event) {
          event = event.originalEvent || event;

          var strPage = "page";
          var strClient = "client";
          var strX = "X";
          var strY = "Y";
          var target = event.target || event.srcElement || document;
          var eventDoc = target.ownerDocument || document;
          var doc = eventDoc.documentElement;
          var body = eventDoc.body;

          //if touch event return return pageX/Y of it
          if (event.touches !== undefined) {
            var touch = event.touches[0];
            return {
              x: touch[strPage + strX],
              y: touch[strPage + strY],
            };
          }

          // Calculate pageX/Y if not native supported
          if (
            !event[strPage + strX] &&
            event[strClient + strX] &&
            event[strClient + strX] != null
          ) {
            return {
              x:
                event[strClient + strX] +
                ((doc && doc.scrollLeft) || (body && body.scrollLeft) || 0) -
                ((doc && doc.clientLeft) || (body && body.clientLeft) || 0),
              y:
                event[strClient + strY] +
                ((doc && doc.scrollTop) || (body && body.scrollTop) || 0) -
                ((doc && doc.clientTop) || (body && body.clientTop) || 0),
            };
          }
          return {
            x: event[strPage + strX],
            y: event[strPage + strY],
          };
        },

        /**
         * Gets the clicked mouse button of the given mouse event.
         * @param event The mouse event of which the clicked button shal be got.
         * @returns {number} The number of the clicked mouse button. (0 : none | 1 : leftButton | 2 : middleButton | 3 : rightButton)
         */
        mBtn: function (event) {
          var button = event.button;
          if (!event.which && button !== undefined)
            return button & 1 ? 1 : button & 2 ? 3 : button & 4 ? 2 : 0;
          else return event.which;
        },

        /**
         * Checks whether a item is in the given array and returns its index.
         * @param item The item of which the position in the array shall be determined.
         * @param arr The array.
         * @returns {number} The zero based index of the item or -1 if the item isn't in the array.
         */
        inA: function (item, arr) {
          for (var i = 0; i < arr[LEXICON.l]; i++)
            //Sometiems in IE a "SCRIPT70" Permission denied error occurs if HTML elements in a iFrame are compared
            try {
              if (arr[i] === item) return i;
            } catch (e) {}
          return -1;
        },

        /**
         * Returns true if the given value is a array.
         * @param arr The potential array.
         * @returns {boolean} True if the given value is a array, false otherwise.
         */
        isA: function (arr) {
          var def = Array.isArray;
          return def ? def(arr) : this.type(arr) == TYPES.a;
        },

        /**
         * Determine the internal JavaScript [[Class]] of the given object.
         * @param obj The object of which the type shall be determined.
         * @returns {string} The type of the given object.
         */
        type: function (obj) {
          if (obj === undefined) return obj + "";
          if (obj === null) return obj + "";
          return Object[LEXICON.p].toString
            .call(obj)
            .replace(/^\[object (.+)\]$/, "$1")
            .toLowerCase();
        },

        bind: bind,

        /**
                 * Gets the vendor-prefixed CSS property by the given name.
                 * For example the given name is "transform" and you're using a old Firefox browser then the returned value would be "-moz-transform".
                 * If the browser doesn't need a vendor-prefix, then the returned string is the given name.
                 * If the browser doesn't support the given property name at all (not even with a vendor-prefix) the returned value is null.
                 * @param propName The unprefixed CSS property name.
                 * @returns {string|null} The vendor-prefixed CSS property or null if the browser doesn't support the given CSS property.

                cssProp: function(propName) {
                    return VENDORS._cssProperty(propName);
                }
                */
      };
    })();

    var MATH = Math;
    var JQUERY = window.jQuery;
    var EASING = (function () {
      var _easingsMath = {
        p: MATH.PI,
        c: MATH.cos,
        s: MATH.sin,
        w: MATH.pow,
        t: MATH.sqrt,
        n: MATH.asin,
        a: MATH.abs,
        o: 1.70158,
      };

      /*
             x : current percent (0 - 1),
             t : current time (duration * percent),
             b : start value (from),
             c : end value (to),
             d : duration

             easingName : function(x, t, b, c, d) { return easedValue; }
             */

      return {
        swing: function (x, t, b, c, d) {
          return 0.5 - _easingsMath.c(x * _easingsMath.p) / 2;
        },
        linear: function (x, t, b, c, d) {
          return x;
        },
        easeInQuad: function (x, t, b, c, d) {
          return c * (t /= d) * t + b;
        },
        easeOutQuad: function (x, t, b, c, d) {
          return -c * (t /= d) * (t - 2) + b;
        },
        easeInOutQuad: function (x, t, b, c, d) {
          return (t /= d / 2) < 1
            ? (c / 2) * t * t + b
            : (-c / 2) * (--t * (t - 2) - 1) + b;
        },
        easeInCubic: function (x, t, b, c, d) {
          return c * (t /= d) * t * t + b;
        },
        easeOutCubic: function (x, t, b, c, d) {
          return c * ((t = t / d - 1) * t * t + 1) + b;
        },
        easeInOutCubic: function (x, t, b, c, d) {
          return (t /= d / 2) < 1
            ? (c / 2) * t * t * t + b
            : (c / 2) * ((t -= 2) * t * t + 2) + b;
        },
        easeInQuart: function (x, t, b, c, d) {
          return c * (t /= d) * t * t * t + b;
        },
        easeOutQuart: function (x, t, b, c, d) {
          return -c * ((t = t / d - 1) * t * t * t - 1) + b;
        },
        easeInOutQuart: function (x, t, b, c, d) {
          return (t /= d / 2) < 1
            ? (c / 2) * t * t * t * t + b
            : (-c / 2) * ((t -= 2) * t * t * t - 2) + b;
        },
        easeInQuint: function (x, t, b, c, d) {
          return c * (t /= d) * t * t * t * t + b;
        },
        easeOutQuint: function (x, t, b, c, d) {
          return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
        },
        easeInOutQuint: function (x, t, b, c, d) {
          return (t /= d / 2) < 1
            ? (c / 2) * t * t * t * t * t + b
            : (c / 2) * ((t -= 2) * t * t * t * t + 2) + b;
        },
        easeInSine: function (x, t, b, c, d) {
          return -c * _easingsMath.c((t / d) * (_easingsMath.p / 2)) + c + b;
        },
        easeOutSine: function (x, t, b, c, d) {
          return c * _easingsMath.s((t / d) * (_easingsMath.p / 2)) + b;
        },
        easeInOutSine: function (x, t, b, c, d) {
          return (-c / 2) * (_easingsMath.c((_easingsMath.p * t) / d) - 1) + b;
        },
        easeInExpo: function (x, t, b, c, d) {
          return t == 0 ? b : c * _easingsMath.w(2, 10 * (t / d - 1)) + b;
        },
        easeOutExpo: function (x, t, b, c, d) {
          return t == d
            ? b + c
            : c * (-_easingsMath.w(2, (-10 * t) / d) + 1) + b;
        },
        easeInOutExpo: function (x, t, b, c, d) {
          if (t == 0) return b;
          if (t == d) return b + c;
          if ((t /= d / 2) < 1)
            return (c / 2) * _easingsMath.w(2, 10 * (t - 1)) + b;
          return (c / 2) * (-_easingsMath.w(2, -10 * --t) + 2) + b;
        },
        easeInCirc: function (x, t, b, c, d) {
          return -c * (_easingsMath.t(1 - (t /= d) * t) - 1) + b;
        },
        easeOutCirc: function (x, t, b, c, d) {
          return c * _easingsMath.t(1 - (t = t / d - 1) * t) + b;
        },
        easeInOutCirc: function (x, t, b, c, d) {
          return (t /= d / 2) < 1
            ? (-c / 2) * (_easingsMath.t(1 - t * t) - 1) + b
            : (c / 2) * (_easingsMath.t(1 - (t -= 2) * t) + 1) + b;
        },
        easeInElastic: function (x, t, b, c, d) {
          var s = _easingsMath.o;
          var p = 0;
          var a = c;
          if (t == 0) return b;
          if ((t /= d) == 1) return b + c;
          if (!p) p = d * 0.3;
          if (a < _easingsMath.a(c)) {
            a = c;
            s = p / 4;
          } else s = (p / (2 * _easingsMath.p)) * _easingsMath.n(c / a);
          return (
            -(
              a *
              _easingsMath.w(2, 10 * (t -= 1)) *
              _easingsMath.s(((t * d - s) * (2 * _easingsMath.p)) / p)
            ) + b
          );
        },
        easeOutElastic: function (x, t, b, c, d) {
          var s = _easingsMath.o;
          var p = 0;
          var a = c;
          if (t == 0) return b;
          if ((t /= d) == 1) return b + c;
          if (!p) p = d * 0.3;
          if (a < _easingsMath.a(c)) {
            a = c;
            s = p / 4;
          } else s = (p / (2 * _easingsMath.p)) * _easingsMath.n(c / a);
          return (
            a *
              _easingsMath.w(2, -10 * t) *
              _easingsMath.s(((t * d - s) * (2 * _easingsMath.p)) / p) +
            c +
            b
          );
        },
        easeInOutElastic: function (x, t, b, c, d) {
          var s = _easingsMath.o;
          var p = 0;
          var a = c;
          if (t == 0) return b;
          if ((t /= d / 2) == 2) return b + c;
          if (!p) p = d * (0.3 * 1.5);
          if (a < _easingsMath.a(c)) {
            a = c;
            s = p / 4;
          } else s = (p / (2 * _easingsMath.p)) * _easingsMath.n(c / a);
          if (t < 1)
            return (
              -0.5 *
                (a *
                  _easingsMath.w(2, 10 * (t -= 1)) *
                  _easingsMath.s(((t * d - s) * (2 * _easingsMath.p)) / p)) +
              b
            );
          return (
            a *
              _easingsMath.w(2, -10 * (t -= 1)) *
              _easingsMath.s(((t * d - s) * (2 * _easingsMath.p)) / p) *
              0.5 +
            c +
            b
          );
        },
        easeInBack: function (x, t, b, c, d, s) {
          s = s || _easingsMath.o;
          return c * (t /= d) * t * ((s + 1) * t - s) + b;
        },
        easeOutBack: function (x, t, b, c, d, s) {
          s = s || _easingsMath.o;
          return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
        },
        easeInOutBack: function (x, t, b, c, d, s) {
          s = s || _easingsMath.o;
          return (t /= d / 2) < 1
            ? (c / 2) * (t * t * (((s *= 1.525) + 1) * t - s)) + b
            : (c / 2) * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b;
        },
        easeInBounce: function (x, t, b, c, d) {
          return c - this.easeOutBounce(x, d - t, 0, c, d) + b;
        },
        easeOutBounce: function (x, t, b, c, d) {
          var o = 7.5625;
          if ((t /= d) < 1 / 2.75) {
            return c * (o * t * t) + b;
          } else if (t < 2 / 2.75) {
            return c * (o * (t -= 1.5 / 2.75) * t + 0.75) + b;
          } else if (t < 2.5 / 2.75) {
            return c * (o * (t -= 2.25 / 2.75) * t + 0.9375) + b;
          } else {
            return c * (o * (t -= 2.625 / 2.75) * t + 0.984375) + b;
          }
        },
        easeInOutBounce: function (x, t, b, c, d) {
          return t < d / 2
            ? this.easeInBounce(x, t * 2, 0, c, d) * 0.5 + b
            : this.easeOutBounce(x, t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
        },
      };
      /*
       *
       * TERMS OF USE - EASING EQUATIONS
       *
       * Open source under the BSD License.
       *
       * Copyright Â© 2001 Robert Penner
       * All rights reserved.
       *
       * Redistribution and use in source and binary forms, with or without modification,
       * are permitted provided that the following conditions are met:
       *
       * Redistributions of source code must retain the above copyright notice, this list of
       * conditions and the following disclaimer.
       * Redistributions in binary form must reproduce the above copyright notice, this list
       * of conditions and the following disclaimer in the documentation and/or other materials
       * provided with the distribution.
       *
       * Neither the name of the author nor the names of contributors may be used to endorse
       * or promote products derived from this software without specific prior written permission.
       *
       * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
       * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
       * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
       *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
       *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
       * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
       * OF THE POSSIBILITY OF SUCH DAMAGE.
       *
       */
    })();
    var FRAMEWORK = (function () {
      var _rnothtmlwhite = /[^\x20\t\r\n\f]+/g;
      var _strSpace = " ";
      var _strEmpty = "";
      var _strScrollLeft = "scrollLeft";
      var _strScrollTop = "scrollTop";
      var _animations = [];
      var _type = COMPATIBILITY.type;
      var _cssNumber = {
        animationIterationCount: true,
        columnCount: true,
        fillOpacity: true,
        flexGrow: true,
        flexShrink: true,
        fontWeight: true,
        lineHeight: true,
        opacity: true,
        order: true,
        orphans: true,
        widows: true,
        zIndex: true,
        zoom: true,
      };

      function extend() {
        var src,
          copyIsArray,
          copy,
          name,
          options,
          clone,
          target = arguments[0] || {},
          i = 1,
          length = arguments[LEXICON.l],
          deep = false;

        // Handle a deep copy situation
        if (_type(target) == TYPES.b) {
          deep = target;
          target = arguments[1] || {};
          // skip the boolean and the target
          i = 2;
        }

        // Handle case when target is a string or something (possible in deep copy)
        if (_type(target) != TYPES.o && !_type(target) == TYPES.f) {
          target = {};
        }

        // extend jQuery itself if only one argument is passed
        if (length === i) {
          target = FakejQuery;
          --i;
        }

        for (; i < length; i++) {
          // Only deal with non-null/undefined values
          if ((options = arguments[i]) != null) {
            // Extend the base object
            for (name in options) {
              src = target[name];
              copy = options[name];

              // Prevent never-ending loop
              if (target === copy) {
                continue;
              }

              // Recurse if we're merging plain objects or arrays
              if (
                deep &&
                copy &&
                (isPlainObject(copy) || (copyIsArray = COMPATIBILITY.isA(copy)))
              ) {
                if (copyIsArray) {
                  copyIsArray = false;
                  clone = src && COMPATIBILITY.isA(src) ? src : [];
                } else {
                  clone = src && isPlainObject(src) ? src : {};
                }

                // Never move original objects, clone them
                target[name] = extend(deep, clone, copy);

                // Don't bring in undefined values
              } else if (copy !== undefined) {
                target[name] = copy;
              }
            }
          }
        }

        // Return the modified object
        return target;
      }

      function inArray(item, arr, fromIndex) {
        for (var i = fromIndex || 0; i < arr[LEXICON.l]; i++)
          if (arr[i] === item) return i;
        return -1;
      }

      function isFunction(obj) {
        return _type(obj) == TYPES.f;
      }

      function isEmptyObject(obj) {
        for (var name in obj) return false;
        return true;
      }

      function isPlainObject(obj) {
        if (!obj || _type(obj) != TYPES.o) return false;

        var key;
        var proto = LEXICON.p;
        var hasOwnProperty = Object[proto].hasOwnProperty;
        var hasOwnConstructor = hasOwnProperty.call(obj, "constructor");
        var hasIsPrototypeOf =
          obj.constructor &&
          obj.constructor[proto] &&
          hasOwnProperty.call(obj.constructor[proto], "isPrototypeOf");

        if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
          return false;
        }

        for (key in obj) {
          /**/
        }

        return _type(key) == TYPES.u || hasOwnProperty.call(obj, key);
      }

      function each(obj, callback) {
        var i = 0;

        if (isArrayLike(obj)) {
          for (; i < obj[LEXICON.l]; i++) {
            if (callback.call(obj[i], i, obj[i]) === false) break;
          }
        } else {
          for (i in obj) {
            if (callback.call(obj[i], i, obj[i]) === false) break;
          }
        }

        return obj;
      }

      function isArrayLike(obj) {
        var length = !!obj && [LEXICON.l] in obj && obj[LEXICON.l];
        var t = _type(obj);
        return isFunction(t)
          ? false
          : t == TYPES.a ||
              length === 0 ||
              (_type(length) == TYPES.n && length > 0 && length - 1 in obj);
      }

      function stripAndCollapse(value) {
        var tokens = value.match(_rnothtmlwhite) || [];
        return tokens.join(_strSpace);
      }

      function matches(elem, selector) {
        var nodeList =
          (elem.parentNode || document).querySelectorAll(selector) || [];
        var i = nodeList[LEXICON.l];

        while (i--) if (nodeList[i] == elem) return true;

        return false;
      }

      function insertAdjacentElement(el, strategy, child) {
        if (COMPATIBILITY.isA(child)) {
          for (var i = 0; i < child[LEXICON.l]; i++)
            insertAdjacentElement(el, strategy, child[i]);
        } else if (_type(child) == TYPES.s)
          el.insertAdjacentHTML(strategy, child);
        else
          el.insertAdjacentElement(strategy, child.nodeType ? child : child[0]);
      }

      function setCSSVal(el, prop, val) {
        try {
          if (el[LEXICON.s][prop] !== undefined)
            el[LEXICON.s][prop] = parseCSSVal(prop, val);
        } catch (e) {}
      }

      function parseCSSVal(prop, val) {
        if (!_cssNumber[prop.toLowerCase()] && _type(val) == TYPES.n)
          val += "px";
        return val;
      }

      function startNextAnimationInQ(animObj, removeFromQ) {
        var index;
        var nextAnim;
        if (removeFromQ !== false) animObj.q.splice(0, 1);
        if (animObj.q[LEXICON.l] > 0) {
          nextAnim = animObj.q[0];
          animate(
            animObj.el,
            nextAnim.props,
            nextAnim.duration,
            nextAnim.easing,
            nextAnim.complete,
            true
          );
        } else {
          index = inArray(animObj, _animations);
          if (index > -1) _animations.splice(index, 1);
        }
      }

      function setAnimationValue(el, prop, value) {
        if (prop === _strScrollLeft || prop === _strScrollTop) el[prop] = value;
        else setCSSVal(el, prop, value);
      }

      function animate(el, props, options, easing, complete, guaranteedNext) {
        var hasOptions = isPlainObject(options);
        var from = {};
        var to = {};
        var i = 0;
        var key;
        var animObj;
        var start;
        var progress;
        var step;
        var specialEasing;
        var duration;
        if (hasOptions) {
          easing = options.easing;
          start = options.start;
          progress = options.progress;
          step = options.step;
          specialEasing = options.specialEasing;
          complete = options.complete;
          duration = options.duration;
        } else duration = options;
        specialEasing = specialEasing || {};
        duration = duration || 400;
        easing = easing || "swing";
        guaranteedNext = guaranteedNext || false;

        for (; i < _animations[LEXICON.l]; i++) {
          if (_animations[i].el === el) {
            animObj = _animations[i];
            break;
          }
        }

        if (!animObj) {
          animObj = {
            el: el,
            q: [],
          };
          _animations.push(animObj);
        }

        for (key in props) {
          if (key === _strScrollLeft || key === _strScrollTop)
            from[key] = el[key];
          else from[key] = FakejQuery(el).css(key);
        }

        for (key in from) {
          if (from[key] !== props[key] && props[key] !== undefined)
            to[key] = props[key];
        }

        if (!isEmptyObject(to)) {
          var timeNow;
          var end;
          var percent;
          var fromVal;
          var toVal;
          var easedVal;
          var timeStart;
          var frame;
          var elapsed;
          var qPos = guaranteedNext ? 0 : inArray(qObj, animObj.q);
          var qObj = {
            props: to,
            duration: hasOptions ? options : duration,
            easing: easing,
            complete: complete,
          };
          if (qPos === -1) {
            qPos = animObj.q[LEXICON.l];
            animObj.q.push(qObj);
          }

          if (qPos === 0) {
            if (duration > 0) {
              timeStart = COMPATIBILITY.now();
              frame = function () {
                timeNow = COMPATIBILITY.now();
                elapsed = timeNow - timeStart;
                end = qObj.stop || elapsed >= duration;
                percent =
                  1 -
                  (MATH.max(0, timeStart + duration - timeNow) / duration || 0);

                for (key in to) {
                  fromVal = parseFloat(from[key]);
                  toVal = parseFloat(to[key]);
                  easedVal =
                    (toVal - fromVal) *
                      EASING[specialEasing[key] || easing](
                        percent,
                        percent * duration,
                        0,
                        1,
                        duration
                      ) +
                    fromVal;
                  setAnimationValue(el, key, easedVal);
                  if (isFunction(step)) {
                    step(easedVal, {
                      elem: el,
                      prop: key,
                      start: fromVal,
                      now: easedVal,
                      end: toVal,
                      pos: percent,
                      options: {
                        easing: easing,
                        speacialEasing: specialEasing,
                        duration: duration,
                        complete: complete,
                        step: step,
                      },
                      startTime: timeStart,
                    });
                  }
                }

                if (isFunction(progress))
                  progress({}, percent, MATH.max(0, duration - elapsed));

                if (end) {
                  startNextAnimationInQ(animObj);
                  if (isFunction(complete)) complete();
                } else qObj.frame = COMPATIBILITY.rAF()(frame);
              };
              qObj.frame = COMPATIBILITY.rAF()(frame);
            } else {
              for (key in to) setAnimationValue(el, key, to[key]);
              startNextAnimationInQ(animObj);
            }
          }
        } else if (guaranteedNext) startNextAnimationInQ(animObj);
      }

      function stop(el, clearQ, jumpToEnd) {
        var animObj;
        var qObj;
        var key;
        var i = 0;
        for (; i < _animations[LEXICON.l]; i++) {
          animObj = _animations[i];
          if (animObj.el === el) {
            if (animObj.q[LEXICON.l] > 0) {
              qObj = animObj.q[0];
              qObj.stop = true;
              COMPATIBILITY.cAF()(qObj.frame);
              animObj.q.splice(0, 1);

              if (jumpToEnd)
                for (key in qObj.props)
                  setAnimationValue(el, key, qObj.props[key]);

              if (clearQ) animObj.q = [];
              else startNextAnimationInQ(animObj, false);
            }
            break;
          }
        }
      }

      function elementIsVisible(el) {
        return !!(
          el[LEXICON.oW] ||
          el[LEXICON.oH] ||
          el.getClientRects()[LEXICON.l]
        );
      }

      function FakejQuery(selector) {
        if (arguments[LEXICON.l] === 0) return this;

        var base = new FakejQuery();
        var elements = selector;
        var i = 0;
        var elms;
        var el;

        if (_type(selector) == TYPES.s) {
          elements = [];
          if (selector.charAt(0) === "<") {
            el = document.createElement("div");
            el.innerHTML = selector;
            elms = el.children;
          } else {
            elms = document.querySelectorAll(selector);
          }

          for (; i < elms[LEXICON.l]; i++) elements.push(elms[i]);
        }

        if (elements) {
          if (
            _type(elements) != TYPES.s &&
            (!isArrayLike(elements) ||
              elements === window ||
              elements === elements.self)
          )
            elements = [elements];

          for (i = 0; i < elements[LEXICON.l]; i++) base[i] = elements[i];

          base[LEXICON.l] = elements[LEXICON.l];
        }

        return base;
      }

      FakejQuery[LEXICON.p] = {
        //EVENTS:

        on: function (eventName, handler) {
          eventName = (eventName || _strEmpty).match(_rnothtmlwhite) || [
            _strEmpty,
          ];

          var eventNameLength = eventName[LEXICON.l];
          var i = 0;
          var el;
          return this.each(function () {
            el = this;
            try {
              if (el.addEventListener) {
                for (; i < eventNameLength; i++)
                  el.addEventListener(eventName[i], handler);
              } else if (el.detachEvent) {
                for (; i < eventNameLength; i++)
                  el.attachEvent("on" + eventName[i], handler);
              }
            } catch (e) {}
          });
        },

        off: function (eventName, handler) {
          eventName = (eventName || _strEmpty).match(_rnothtmlwhite) || [
            _strEmpty,
          ];

          var eventNameLength = eventName[LEXICON.l];
          var i = 0;
          var el;
          return this.each(function () {
            el = this;
            try {
              if (el.removeEventListener) {
                for (; i < eventNameLength; i++)
                  el.removeEventListener(eventName[i], handler);
              } else if (el.detachEvent) {
                for (; i < eventNameLength; i++)
                  el.detachEvent("on" + eventName[i], handler);
              }
            } catch (e) {}
          });
        },

        one: function (eventName, handler) {
          eventName = (eventName || _strEmpty).match(_rnothtmlwhite) || [
            _strEmpty,
          ];
          return this.each(function () {
            var el = FakejQuery(this);
            FakejQuery.each(eventName, function (i, oneEventName) {
              var oneHandler = function (e) {
                handler.call(this, e);
                el.off(oneEventName, oneHandler);
              };
              el.on(oneEventName, oneHandler);
            });
          });
        },

        trigger: function (eventName) {
          var el;
          var event;
          return this.each(function () {
            el = this;
            if (document.createEvent) {
              event = document.createEvent("HTMLEvents");
              event.initEvent(eventName, true, false);
              el.dispatchEvent(event);
            } else {
              el.fireEvent("on" + eventName);
            }
          });
        },

        //DOM NODE INSERTING / REMOVING:

        append: function (child) {
          return this.each(function () {
            insertAdjacentElement(this, "beforeend", child);
          });
        },

        prepend: function (child) {
          return this.each(function () {
            insertAdjacentElement(this, "afterbegin", child);
          });
        },

        before: function (child) {
          return this.each(function () {
            insertAdjacentElement(this, "beforebegin", child);
          });
        },

        after: function (child) {
          return this.each(function () {
            insertAdjacentElement(this, "afterend", child);
          });
        },

        remove: function () {
          return this.each(function () {
            var el = this;
            var parentNode = el.parentNode;
            if (parentNode != null) parentNode.removeChild(el);
          });
        },

        unwrap: function () {
          var parents = [];
          var i;
          var el;
          var parent;

          this.each(function () {
            parent = this.parentNode;
            if (inArray(parent, parents) === -1) parents.push(parent);
          });

          for (i = 0; i < parents[LEXICON.l]; i++) {
            el = parents[i];
            parent = el.parentNode;
            while (el.firstChild) parent.insertBefore(el.firstChild, el);
            parent.removeChild(el);
          }

          return this;
        },

        wrapAll: function (wrapperHTML) {
          var i;
          var nodes = this;
          var wrapper = FakejQuery(wrapperHTML)[0];
          var deepest = wrapper;
          var parent = nodes[0].parentNode;
          var previousSibling = nodes[0].previousSibling;
          while (deepest.childNodes[LEXICON.l] > 0)
            deepest = deepest.childNodes[0];

          for (
            i = 0;
            nodes[LEXICON.l] - i;
            deepest.firstChild === nodes[0] && i++
          )
            deepest.appendChild(nodes[i]);

          var nextSibling = previousSibling
            ? previousSibling.nextSibling
            : parent.firstChild;
          parent.insertBefore(wrapper, nextSibling);

          return this;
        },

        wrapInner: function (wrapperHTML) {
          return this.each(function () {
            var el = FakejQuery(this);
            var contents = el.contents();

            if (contents[LEXICON.l]) contents.wrapAll(wrapperHTML);
            else el.append(wrapperHTML);
          });
        },

        wrap: function (wrapperHTML) {
          return this.each(function () {
            FakejQuery(this).wrapAll(wrapperHTML);
          });
        },

        //DOM NODE MANIPULATION / INFORMATION:

        css: function (styles, val) {
          var el;
          var key;
          var cptStyle;
          var getCptStyle = window.getComputedStyle;
          if (_type(styles) == TYPES.s) {
            if (val === undefined) {
              el = this[0];
              cptStyle = getCptStyle
                ? getCptStyle(el, null)
                : el.currentStyle[styles];

              //https://bugzilla.mozilla.org/show_bug.cgi?id=548397 can be null sometimes if iframe with display: none (firefox only!)
              return getCptStyle
                ? cptStyle != null
                  ? cptStyle.getPropertyValue(styles)
                  : el[LEXICON.s][styles]
                : cptStyle;
            } else {
              return this.each(function () {
                setCSSVal(this, styles, val);
              });
            }
          } else {
            return this.each(function () {
              for (key in styles) setCSSVal(this, key, styles[key]);
            });
          }
        },

        hasClass: function (className) {
          var elem,
            i = 0;
          var classNamePrepared = _strSpace + className + _strSpace;
          var classList;

          while ((elem = this[i++])) {
            classList = elem.classList;
            if (classList && classList.contains(className)) return true;
            else if (
              elem.nodeType === 1 &&
              (
                _strSpace +
                stripAndCollapse(elem.className + _strEmpty) +
                _strSpace
              ).indexOf(classNamePrepared) > -1
            )
              return true;
          }

          return false;
        },

        addClass: function (className) {
          var classes;
          var elem;
          var cur;
          var curValue;
          var clazz;
          var finalValue;
          var supportClassList;
          var elmClassList;
          var i = 0;
          var v = 0;

          if (className) {
            classes = className.match(_rnothtmlwhite) || [];

            while ((elem = this[i++])) {
              elmClassList = elem.classList;
              if (supportClassList === undefined)
                supportClassList = elmClassList !== undefined;

              if (supportClassList) {
                while ((clazz = classes[v++])) elmClassList.add(clazz);
              } else {
                curValue = elem.className + _strEmpty;
                cur =
                  elem.nodeType === 1 &&
                  _strSpace + stripAndCollapse(curValue) + _strSpace;

                if (cur) {
                  while ((clazz = classes[v++]))
                    if (cur.indexOf(_strSpace + clazz + _strSpace) < 0)
                      cur += clazz + _strSpace;

                  finalValue = stripAndCollapse(cur);
                  if (curValue !== finalValue) elem.className = finalValue;
                }
              }
            }
          }

          return this;
        },

        removeClass: function (className) {
          var classes;
          var elem;
          var cur;
          var curValue;
          var clazz;
          var finalValue;
          var supportClassList;
          var elmClassList;
          var i = 0;
          var v = 0;

          if (className) {
            classes = className.match(_rnothtmlwhite) || [];

            while ((elem = this[i++])) {
              elmClassList = elem.classList;
              if (supportClassList === undefined)
                supportClassList = elmClassList !== undefined;

              if (supportClassList) {
                while ((clazz = classes[v++])) elmClassList.remove(clazz);
              } else {
                curValue = elem.className + _strEmpty;
                cur =
                  elem.nodeType === 1 &&
                  _strSpace + stripAndCollapse(curValue) + _strSpace;

                if (cur) {
                  while ((clazz = classes[v++]))
                    while (cur.indexOf(_strSpace + clazz + _strSpace) > -1)
                      cur = cur.replace(
                        _strSpace + clazz + _strSpace,
                        _strSpace
                      );

                  finalValue = stripAndCollapse(cur);
                  if (curValue !== finalValue) elem.className = finalValue;
                }
              }
            }
          }

          return this;
        },

        hide: function () {
          return this.each(function () {
            this[LEXICON.s].display = "none";
          });
        },

        show: function () {
          return this.each(function () {
            this[LEXICON.s].display = "block";
          });
        },

        attr: function (attrName, value) {
          var i = 0;
          var el;
          while ((el = this[i++])) {
            if (value === undefined) return el.getAttribute(attrName);
            el.setAttribute(attrName, value);
          }
          return this;
        },

        removeAttr: function (attrName) {
          return this.each(function () {
            this.removeAttribute(attrName);
          });
        },

        offset: function () {
          var el = this[0];
          var rect = el[LEXICON.bCR]();
          var scrollLeft =
            window.pageXOffset || document.documentElement[_strScrollLeft];
          var scrollTop =
            window.pageYOffset || document.documentElement[_strScrollTop];
          return {
            top: rect.top + scrollTop,
            left: rect.left + scrollLeft,
          };
        },

        position: function () {
          var el = this[0];
          return {
            top: el.offsetTop,
            left: el.offsetLeft,
          };
        },

        scrollLeft: function (value) {
          var i = 0;
          var el;
          while ((el = this[i++])) {
            if (value === undefined) return el[_strScrollLeft];
            el[_strScrollLeft] = value;
          }
          return this;
        },

        scrollTop: function (value) {
          var i = 0;
          var el;
          while ((el = this[i++])) {
            if (value === undefined) return el[_strScrollTop];
            el[_strScrollTop] = value;
          }
          return this;
        },

        val: function (value) {
          var el = this[0];
          if (!value) return el.value;
          el.value = value;
          return this;
        },

        //DOM TRAVERSAL / FILTERING:

        first: function () {
          return this.eq(0);
        },

        last: function () {
          return this.eq(-1);
        },

        eq: function (index) {
          return FakejQuery(this[index >= 0 ? index : this[LEXICON.l] + index]);
        },

        find: function (selector) {
          var children = [];
          var i;
          this.each(function () {
            var el = this;
            var ch = el.querySelectorAll(selector);
            for (i = 0; i < ch[LEXICON.l]; i++) children.push(ch[i]);
          });
          return FakejQuery(children);
        },

        children: function (selector) {
          var children = [];
          var el;
          var ch;
          var i;

          this.each(function () {
            ch = this.children;
            for (i = 0; i < ch[LEXICON.l]; i++) {
              el = ch[i];
              if (selector) {
                if (
                  (el.matches && el.matches(selector)) ||
                  matches(el, selector)
                )
                  children.push(el);
              } else children.push(el);
            }
          });
          return FakejQuery(children);
        },

        parent: function (selector) {
          var parents = [];
          var parent;
          this.each(function () {
            parent = this.parentNode;
            if (selector ? FakejQuery(parent).is(selector) : true)
              parents.push(parent);
          });
          return FakejQuery(parents);
        },

        is: function (selector) {
          var el;
          var i;
          for (i = 0; i < this[LEXICON.l]; i++) {
            el = this[i];
            if (selector === ":visible") return elementIsVisible(el);
            if (selector === ":hidden") return !elementIsVisible(el);
            if ((el.matches && el.matches(selector)) || matches(el, selector))
              return true;
          }
          return false;
        },

        contents: function () {
          var contents = [];
          var childs;
          var i;

          this.each(function () {
            childs = this.childNodes;
            for (i = 0; i < childs[LEXICON.l]; i++) contents.push(childs[i]);
          });

          return FakejQuery(contents);
        },

        each: function (callback) {
          return each(this, callback);
        },

        //ANIMATION:

        animate: function (props, duration, easing, complete) {
          return this.each(function () {
            animate(this, props, duration, easing, complete);
          });
        },

        stop: function (clearQ, jump) {
          return this.each(function () {
            stop(this, clearQ, jump);
          });
        },
      };

      extend(FakejQuery, {
        extend: extend,
        inArray: inArray,
        isEmptyObject: isEmptyObject,
        isPlainObject: isPlainObject,
        each: each,
      });

      return FakejQuery;
    })();
    var INSTANCES = (function () {
      var _targets = [];
      var _instancePropertyString = "__overlayScrollbars__";

      /**
       * Register, unregister or get a certain (or all) instances.
       * Register: Pass the target and the instance.
       * Unregister: Pass the target and null.
       * Get Instance: Pass the target from which the instance shall be got.
       * Get Targets: Pass no arguments.
       * @param target The target to which the instance shall be registered / from which the instance shall be unregistered / the instance shall be got
       * @param instance The instance.
       * @returns {*|void} Returns the instance from the given target.
       */
      return function (target, instance) {
        var argLen = arguments[LEXICON.l];
        if (argLen < 1) {
          //return all targets
          return _targets;
        } else {
          if (instance) {
            //register instance
            target[_instancePropertyString] = instance;
            _targets.push(target);
          } else {
            var index = COMPATIBILITY.inA(target, _targets);
            if (index > -1) {
              if (argLen > 1) {
                //unregister instance
                delete target[_instancePropertyString];
                _targets.splice(index, 1);
              } else {
                //get instance from target
                return _targets[index][_instancePropertyString];
              }
            }
          }
        }
      };
    })();
    var PLUGIN = (function () {
      var _plugin;
      var _pluginsGlobals;
      var _pluginsAutoUpdateLoop;
      var _pluginsExtensions = [];
      var _pluginsOptions = (function () {
        var type = COMPATIBILITY.type;
        var possibleTemplateTypes = [
          TYPES.b, //boolean
          TYPES.n, //number
          TYPES.s, //string
          TYPES.a, //array
          TYPES.o, //object
          TYPES.f, //function
          TYPES.z, //null
        ];
        var restrictedStringsSplit = " ";
        var restrictedStringsPossibilitiesSplit = ":";
        var classNameAllowedValues = [TYPES.z, TYPES.s];
        var numberAllowedValues = TYPES.n;
        var booleanNullAllowedValues = [TYPES.z, TYPES.b];
        var booleanTrueTemplate = [true, TYPES.b];
        var booleanFalseTemplate = [false, TYPES.b];
        var callbackTemplate = [null, [TYPES.z, TYPES.f]];
        var updateOnLoadTemplate = [["img"], [TYPES.s, TYPES.a, TYPES.z]];
        var inheritedAttrsTemplate = [
          ["style", "class"],
          [TYPES.s, TYPES.a, TYPES.z],
        ];
        var resizeAllowedValues = "n:none b:both h:horizontal v:vertical";
        var overflowBehaviorAllowedValues =
          "v-h:visible-hidden v-s:visible-scroll s:scroll h:hidden";
        var scrollbarsVisibilityAllowedValues = "v:visible h:hidden a:auto";
        var scrollbarsAutoHideAllowedValues = "n:never s:scroll l:leave m:move";
        var optionsDefaultsAndTemplate = {
          className: ["os-theme-dark", classNameAllowedValues], //null || string
          resize: ["none", resizeAllowedValues], //none || both  || horizontal || vertical || n || b || h || v
          sizeAutoCapable: booleanTrueTemplate, //true || false
          clipAlways: booleanTrueTemplate, //true || false
          normalizeRTL: booleanTrueTemplate, //true || false
          paddingAbsolute: booleanFalseTemplate, //true || false
          autoUpdate: [null, booleanNullAllowedValues], //true || false || null
          autoUpdateInterval: [33, numberAllowedValues], //number
          updateOnLoad: updateOnLoadTemplate, //string || array || null
          nativeScrollbarsOverlaid: {
            showNativeScrollbars: booleanFalseTemplate, //true || false
            initialize: booleanTrueTemplate, //true || false
          },
          overflowBehavior: {
            x: ["scroll", overflowBehaviorAllowedValues], //visible-hidden  || visible-scroll || hidden || scroll || v-h || v-s || h || s
            y: ["scroll", overflowBehaviorAllowedValues], //visible-hidden  || visible-scroll || hidden || scroll || v-h || v-s || h || s
          },
          scrollbars: {
            visibility: ["auto", scrollbarsVisibilityAllowedValues], //visible || hidden || auto || v || h || a
            autoHide: ["never", scrollbarsAutoHideAllowedValues], //never || scroll || leave || move || n || s || l || m
            autoHideDelay: [800, numberAllowedValues], //number
            dragScrolling: booleanTrueTemplate, //true || false
            clickScrolling: booleanFalseTemplate, //true || false
            touchSupport: booleanTrueTemplate, //true || false
            snapHandle: booleanFalseTemplate, //true || false
          },
          textarea: {
            dynWidth: booleanFalseTemplate, //true || false
            dynHeight: booleanFalseTemplate, //true || false
            inheritedAttrs: inheritedAttrsTemplate, //string || array || null
          },
          callbacks: {
            onInitialized: callbackTemplate, //null || function
            onInitializationWithdrawn: callbackTemplate, //null || function
            onDestroyed: callbackTemplate, //null || function
            onScrollStart: callbackTemplate, //null || function
            onScroll: callbackTemplate, //null || function
            onScrollStop: callbackTemplate, //null || function
            onOverflowChanged: callbackTemplate, //null || function
            onOverflowAmountChanged: callbackTemplate, //null || function
            onDirectionChanged: callbackTemplate, //null || function
            onContentSizeChanged: callbackTemplate, //null || function
            onHostSizeChanged: callbackTemplate, //null || function
            onUpdated: callbackTemplate, //null || function
          },
        };
        var convert = function (template) {
          var recursive = function (obj) {
            var key;
            var val;
            var valType;
            for (key in obj) {
              if (!obj[LEXICON.hOP](key)) continue;
              val = obj[key];
              valType = type(val);
              if (valType == TYPES.a) obj[key] = val[template ? 1 : 0];
              else if (valType == TYPES.o) obj[key] = recursive(val);
            }
            return obj;
          };
          return recursive(
            FRAMEWORK.extend(true, {}, optionsDefaultsAndTemplate)
          );
        };

        return {
          _defaults: convert(),

          _template: convert(true),

          /**
           * Validates the passed object by the passed template.
           * @param obj The object which shall be validated.
           * @param template The template which defines the allowed values and types.
           * @param writeErrors True if errors shall be logged to the console.
           * @param diffObj If a object is passed then only valid differences to this object will be returned.
           * @returns {{}} A object which contains two objects called "default" and "prepared" which contains only the valid properties of the passed original object and discards not different values compared to the passed diffObj.
           */
          _validate: function (obj, template, writeErrors, diffObj) {
            var validatedOptions = {};
            var validatedOptionsPrepared = {};
            var objectCopy = FRAMEWORK.extend(true, {}, obj);
            var inArray = FRAMEWORK.inArray;
            var isEmptyObj = FRAMEWORK.isEmptyObject;
            var checkObjectProps = function (
              data,
              template,
              diffData,
              validatedOptions,
              validatedOptionsPrepared,
              prevPropName
            ) {
              for (var prop in template) {
                if (template[LEXICON.hOP](prop) && data[LEXICON.hOP](prop)) {
                  var isValid = false;
                  var isDiff = false;
                  var templateValue = template[prop];
                  var templateValueType = type(templateValue);
                  var templateIsComplex = templateValueType == TYPES.o;
                  var templateTypes = !COMPATIBILITY.isA(templateValue)
                    ? [templateValue]
                    : templateValue;
                  var dataDiffValue = diffData[prop];
                  var dataValue = data[prop];
                  var dataValueType = type(dataValue);
                  var propPrefix = prevPropName ? prevPropName + "." : "";
                  var error =
                    'The option "' +
                    propPrefix +
                    prop +
                    "\" wasn't set, because";
                  var errorPossibleTypes = [];
                  var errorRestrictedStrings = [];
                  var restrictedStringValuesSplit;
                  var restrictedStringValuesPossibilitiesSplit;
                  var isRestrictedValue;
                  var mainPossibility;
                  var currType;
                  var i;
                  var v;
                  var j;

                  dataDiffValue =
                    dataDiffValue === undefined ? {} : dataDiffValue;

                  //if the template has a object as value, it means that the options are complex (verschachtelt)
                  if (templateIsComplex && dataValueType == TYPES.o) {
                    validatedOptions[prop] = {};
                    validatedOptionsPrepared[prop] = {};
                    checkObjectProps(
                      dataValue,
                      templateValue,
                      dataDiffValue,
                      validatedOptions[prop],
                      validatedOptionsPrepared[prop],
                      propPrefix + prop
                    );
                    FRAMEWORK.each(
                      [data, validatedOptions, validatedOptionsPrepared],
                      function (index, value) {
                        if (isEmptyObj(value[prop])) {
                          delete value[prop];
                        }
                      }
                    );
                  } else if (!templateIsComplex) {
                    for (i = 0; i < templateTypes[LEXICON.l]; i++) {
                      currType = templateTypes[i];
                      templateValueType = type(currType);
                      //if currtype is string and starts with restrictedStringPrefix and end with restrictedStringSuffix
                      isRestrictedValue =
                        templateValueType == TYPES.s &&
                        inArray(currType, possibleTemplateTypes) === -1;
                      if (isRestrictedValue) {
                        errorPossibleTypes.push(TYPES.s);

                        //split it into a array which contains all possible values for example: ["y:yes", "n:no", "m:maybe"]
                        restrictedStringValuesSplit = currType.split(
                          restrictedStringsSplit
                        );
                        errorRestrictedStrings = errorRestrictedStrings.concat(
                          restrictedStringValuesSplit
                        );
                        for (
                          v = 0;
                          v < restrictedStringValuesSplit[LEXICON.l];
                          v++
                        ) {
                          //split the possible values into their possibiliteis for example: ["y", "yes"] -> the first is always the mainPossibility
                          restrictedStringValuesPossibilitiesSplit =
                            restrictedStringValuesSplit[v].split(
                              restrictedStringsPossibilitiesSplit
                            );
                          mainPossibility =
                            restrictedStringValuesPossibilitiesSplit[0];
                          for (
                            j = 0;
                            j <
                            restrictedStringValuesPossibilitiesSplit[LEXICON.l];
                            j++
                          ) {
                            //if any possibility matches with the dataValue, its valid
                            if (
                              dataValue ===
                              restrictedStringValuesPossibilitiesSplit[j]
                            ) {
                              isValid = true;
                              break;
                            }
                          }
                          if (isValid) break;
                        }
                      } else {
                        errorPossibleTypes.push(currType);

                        if (dataValueType === currType) {
                          isValid = true;
                          break;
                        }
                      }
                    }

                    if (isValid) {
                      isDiff = dataValue !== dataDiffValue;

                      if (isDiff) validatedOptions[prop] = dataValue;

                      if (
                        isRestrictedValue
                          ? inArray(
                              dataDiffValue,
                              restrictedStringValuesPossibilitiesSplit
                            ) < 0
                          : isDiff
                      )
                        validatedOptionsPrepared[prop] = isRestrictedValue
                          ? mainPossibility
                          : dataValue;
                    } else if (writeErrors) {
                      console.warn(
                        error +
                          " it doesn't accept the type [ " +
                          dataValueType.toUpperCase() +
                          ' ] with the value of "' +
                          dataValue +
                          '".\r\n' +
                          "Accepted types are: [ " +
                          errorPossibleTypes.join(", ").toUpperCase() +
                          " ]." +
                          (errorRestrictedStrings[length] > 0
                            ? "\r\nValid strings are: [ " +
                              errorRestrictedStrings
                                .join(", ")
                                .split(restrictedStringsPossibilitiesSplit)
                                .join(", ") +
                              " ]."
                            : "")
                      );
                    }
                    delete data[prop];
                  }
                }
              }
            };
            checkObjectProps(
              objectCopy,
              template,
              diffObj || {},
              validatedOptions,
              validatedOptionsPrepared
            );

            //add values which aren't specified in the template to the finished validated object to prevent them from being discarded
            /*
                        if(keepForeignProps) {
                            FRAMEWORK.extend(true, validatedOptions, objectCopy);
                            FRAMEWORK.extend(true, validatedOptionsPrepared, objectCopy);
                        }
                        */

            if (!isEmptyObj(objectCopy) && writeErrors)
              console.warn(
                "The following options are discarded due to invalidity:\r\n" +
                  window.JSON.stringify(objectCopy, null, 2)
              );

            return {
              _default: validatedOptions,
              _prepared: validatedOptionsPrepared,
            };
          },
        };
      })();

      /**
       * Initializes the object which contains global information about the plugin and each instance of it.
       */
      function initOverlayScrollbarsStatics() {
        if (!_pluginsGlobals)
          _pluginsGlobals = new OverlayScrollbarsGlobals(
            _pluginsOptions._defaults
          );
        if (!_pluginsAutoUpdateLoop)
          _pluginsAutoUpdateLoop = new OverlayScrollbarsAutoUpdateLoop(
            _pluginsGlobals
          );
      }

      /**
       * The global object for the OverlayScrollbars objects. It contains resources which every OverlayScrollbars object needs. This object is initialized only once: if the first OverlayScrollbars object gets initialized.
       * @param defaultOptions
       * @constructor
       */
      function OverlayScrollbarsGlobals(defaultOptions) {
        var _base = this;
        var strOverflow = "overflow";
        var strHidden = "hidden";
        var strScroll = "scroll";
        var bodyElement = FRAMEWORK("body");
        var scrollbarDummyElement = FRAMEWORK(
          '<div id="os-dummy-scrollbar-size"><div></div></div>'
        );
        var scrollbarDummyElement0 = scrollbarDummyElement[0];
        var dummyContainerChild = FRAMEWORK(
          scrollbarDummyElement.children("div").eq(0)
        );

        bodyElement.append(scrollbarDummyElement);
        scrollbarDummyElement.hide().show(); //fix IE8 bug (incorrect measuring)

        var nativeScrollbarSize = calcNativeScrollbarSize(
          scrollbarDummyElement0
        );
        var nativeScrollbarIsOverlaid = {
          x: nativeScrollbarSize.x === 0,
          y: nativeScrollbarSize.y === 0,
        };
        var msie = (function () {
          var ua = window.navigator.userAgent;
          var strIndexOf = "indexOf";
          var strSubString = "substring";
          var msie = ua[strIndexOf]("MSIE ");
          var trident = ua[strIndexOf]("Trident/");
          var edge = ua[strIndexOf]("Edge/");
          var rv = ua[strIndexOf]("rv:");
          var result;
          var parseIntFunc = parseInt;

          // IE 10 or older => return version number
          if (msie > 0)
            result = parseIntFunc(
              ua[strSubString](msie + 5, ua[strIndexOf](".", msie)),
              10
            );
          // IE 11 => return version number
          else if (trident > 0)
            result = parseIntFunc(
              ua[strSubString](rv + 3, ua[strIndexOf](".", rv)),
              10
            );
          // Edge (IE 12+) => return version number
          else if (edge > 0)
            result = parseIntFunc(
              ua[strSubString](edge + 5, ua[strIndexOf](".", edge)),
              10
            );

          // other browser
          return result;
        })();

        FRAMEWORK.extend(_base, {
          defaultOptions: defaultOptions,
          msie: msie,
          autoUpdateLoop: false,
          autoUpdateRecommended: !COMPATIBILITY.mO(),
          nativeScrollbarSize: nativeScrollbarSize,
          nativeScrollbarIsOverlaid: nativeScrollbarIsOverlaid,
          nativeScrollbarStyling: (function () {
            var result = false;
            scrollbarDummyElement.addClass(
              "os-viewport-native-scrollbars-invisible"
            );
            try {
              result =
                (scrollbarDummyElement.css("scrollbar-width") === "none" &&
                  (msie > 9 || !msie)) ||
                window
                  .getComputedStyle(
                    scrollbarDummyElement0,
                    "::-webkit-scrollbar"
                  )
                  .getPropertyValue("display") === "none";
            } catch (ex) {}

            //fix opera bug: scrollbar styles will only appear if overflow value is scroll or auto during the activation of the style.
            //and set overflow to scroll
            //scrollbarDummyElement.css(strOverflow, strHidden).hide().css(strOverflow, strScroll).show();
            //return (scrollbarDummyElement0[LEXICON.oH] - scrollbarDummyElement0[LEXICON.cH]) === 0 && (scrollbarDummyElement0[LEXICON.oW] - scrollbarDummyElement0[LEXICON.cW]) === 0;

            return result;
          })(),
          overlayScrollbarDummySize: { x: 30, y: 30 },
          cssCalc: VENDORS._cssPropertyValue("width", "calc", "(1px)") || null,
          restrictedMeasuring: (function () {
            //https://bugzilla.mozilla.org/show_bug.cgi?id=1439305
            //since 1.11.0 always false -> fixed via CSS (hopefully)
            scrollbarDummyElement.css(strOverflow, strHidden);
            var scrollSize = {
              w: scrollbarDummyElement0[LEXICON.sW],
              h: scrollbarDummyElement0[LEXICON.sH],
            };
            scrollbarDummyElement.css(strOverflow, "visible");
            var scrollSize2 = {
              w: scrollbarDummyElement0[LEXICON.sW],
              h: scrollbarDummyElement0[LEXICON.sH],
            };
            return (
              scrollSize.w - scrollSize2.w !== 0 ||
              scrollSize.h - scrollSize2.h !== 0
            );
          })(),
          rtlScrollBehavior: (function () {
            scrollbarDummyElement
              .css({
                "overflow-y": strHidden,
                "overflow-x": strScroll,
                direction: "rtl",
              })
              .scrollLeft(0);
            var dummyContainerOffset = scrollbarDummyElement.offset();
            var dummyContainerChildOffset = dummyContainerChild.offset();
            //https://github.com/KingSora/OverlayScrollbars/issues/187
            scrollbarDummyElement.scrollLeft(-999);
            var dummyContainerChildOffsetAfterScroll =
              dummyContainerChild.offset();
            return {
              //origin direction = determines if the zero scroll position is on the left or right side
              //'i' means 'invert' (i === true means that the axis must be inverted to be correct)
              //true = on the left side
              //false = on the right side
              i: dummyContainerOffset.left === dummyContainerChildOffset.left,
              //negative = determines if the maximum scroll is positive or negative
              //'n' means 'negate' (n === true means that the axis must be negated to be correct)
              //true = negative
              //false = positive
              n:
                dummyContainerChildOffset.left !==
                dummyContainerChildOffsetAfterScroll.left,
            };
          })(),
          supportTransform: !!VENDORS._cssProperty("transform"),
          supportTransition: !!VENDORS._cssProperty("transition"),
          supportPassiveEvents: (function () {
            var supportsPassive = false;
            try {
              window.addEventListener(
                "test",
                null,
                Object.defineProperty({}, "passive", {
                  get: function () {
                    supportsPassive = true;
                  },
                })
              );
            } catch (e) {}
            return supportsPassive;
          })(),
          supportResizeObserver: !!COMPATIBILITY.rO(),
          supportMutationObserver: !!COMPATIBILITY.mO(),
        });

        scrollbarDummyElement.removeAttr(LEXICON.s).remove();

        //Catch zoom event:
        (function () {
          if (nativeScrollbarIsOverlaid.x && nativeScrollbarIsOverlaid.y)
            return;

          var abs = MATH.abs;
          var windowWidth = COMPATIBILITY.wW();
          var windowHeight = COMPATIBILITY.wH();
          var windowDpr = getWindowDPR();
          var onResize = function () {
            if (INSTANCES().length > 0) {
              var newW = COMPATIBILITY.wW();
              var newH = COMPATIBILITY.wH();
              var deltaW = newW - windowWidth;
              var deltaH = newH - windowHeight;

              if (deltaW === 0 && deltaH === 0) return;

              var deltaWRatio = MATH.round(newW / (windowWidth / 100.0));
              var deltaHRatio = MATH.round(newH / (windowHeight / 100.0));
              var absDeltaW = abs(deltaW);
              var absDeltaH = abs(deltaH);
              var absDeltaWRatio = abs(deltaWRatio);
              var absDeltaHRatio = abs(deltaHRatio);
              var newDPR = getWindowDPR();

              var deltaIsBigger = absDeltaW > 2 && absDeltaH > 2;
              var difference = !differenceIsBiggerThanOne(
                absDeltaWRatio,
                absDeltaHRatio
              );
              var dprChanged = newDPR !== windowDpr && windowDpr > 0;
              var isZoom = deltaIsBigger && difference && dprChanged;
              var oldScrollbarSize = _base.nativeScrollbarSize;
              var newScrollbarSize;

              if (isZoom) {
                bodyElement.append(scrollbarDummyElement);
                newScrollbarSize = _base.nativeScrollbarSize =
                  calcNativeScrollbarSize(scrollbarDummyElement[0]);
                scrollbarDummyElement.remove();
                if (
                  oldScrollbarSize.x !== newScrollbarSize.x ||
                  oldScrollbarSize.y !== newScrollbarSize.y
                ) {
                  FRAMEWORK.each(INSTANCES(), function () {
                    if (INSTANCES(this)) INSTANCES(this).update("zoom");
                  });
                }
              }

              windowWidth = newW;
              windowHeight = newH;
              windowDpr = newDPR;
            }
          };

          function differenceIsBiggerThanOne(valOne, valTwo) {
            var absValOne = abs(valOne);
            var absValTwo = abs(valTwo);
            return !(
              absValOne === absValTwo ||
              absValOne + 1 === absValTwo ||
              absValOne - 1 === absValTwo
            );
          }

          function getWindowDPR() {
            var dDPI = window.screen.deviceXDPI || 0;
            var sDPI = window.screen.logicalXDPI || 1;
            return window.devicePixelRatio || dDPI / sDPI;
          }

          FRAMEWORK(window).on("resize", onResize);
        })();

        function calcNativeScrollbarSize(measureElement) {
          return {
            x: measureElement[LEXICON.oH] - measureElement[LEXICON.cH],
            y: measureElement[LEXICON.oW] - measureElement[LEXICON.cW],
          };
        }
      }

      /**
       * The object which manages the auto update loop for all OverlayScrollbars objects. This object is initialized only once: if the first OverlayScrollbars object gets initialized.
       * @constructor
       */
      function OverlayScrollbarsAutoUpdateLoop(globals) {
        var _base = this;
        var _inArray = FRAMEWORK.inArray;
        var _getNow = COMPATIBILITY.now;
        var _strAutoUpdate = "autoUpdate";
        var _strAutoUpdateInterval = _strAutoUpdate + "Interval";
        var _strLength = LEXICON.l;
        var _loopingInstances = [];
        var _loopingInstancesIntervalCache = [];
        var _loopIsActive = false;
        var _loopIntervalDefault = 33;
        var _loopInterval = _loopIntervalDefault;
        var _loopTimeOld = _getNow();
        var _loopID;

        /**
         * The auto update loop which will run every 50 milliseconds or less if the update interval of a instance is lower than 50 milliseconds.
         */
        var loop = function () {
          if (_loopingInstances[_strLength] > 0 && _loopIsActive) {
            _loopID = COMPATIBILITY.rAF()(function () {
              loop();
            });
            var timeNew = _getNow();
            var timeDelta = timeNew - _loopTimeOld;
            var lowestInterval;
            var instance;
            var instanceOptions;
            var instanceAutoUpdateAllowed;
            var instanceAutoUpdateInterval;
            var now;

            if (timeDelta > _loopInterval) {
              _loopTimeOld = timeNew - (timeDelta % _loopInterval);
              lowestInterval = _loopIntervalDefault;
              for (var i = 0; i < _loopingInstances[_strLength]; i++) {
                instance = _loopingInstances[i];
                if (instance !== undefined) {
                  instanceOptions = instance.options();
                  instanceAutoUpdateAllowed = instanceOptions[_strAutoUpdate];
                  instanceAutoUpdateInterval = MATH.max(
                    1,
                    instanceOptions[_strAutoUpdateInterval]
                  );
                  now = _getNow();

                  if (
                    (instanceAutoUpdateAllowed === true ||
                      instanceAutoUpdateAllowed === null) &&
                    now - _loopingInstancesIntervalCache[i] >
                      instanceAutoUpdateInterval
                  ) {
                    instance.update("auto");
                    _loopingInstancesIntervalCache[i] = new Date(
                      (now += instanceAutoUpdateInterval)
                    );
                  }

                  lowestInterval = MATH.max(
                    1,
                    MATH.min(lowestInterval, instanceAutoUpdateInterval)
                  );
                }
              }
              _loopInterval = lowestInterval;
            }
          } else {
            _loopInterval = _loopIntervalDefault;
          }
        };

        /**
         * Add OverlayScrollbars instance to the auto update loop. Only successful if the instance isn't already added.
         * @param instance The instance which shall be updated in a loop automatically.
         */
        _base.add = function (instance) {
          if (_inArray(instance, _loopingInstances) === -1) {
            _loopingInstances.push(instance);
            _loopingInstancesIntervalCache.push(_getNow());
            if (_loopingInstances[_strLength] > 0 && !_loopIsActive) {
              _loopIsActive = true;
              globals.autoUpdateLoop = _loopIsActive;
              loop();
            }
          }
        };

        /**
         * Remove OverlayScrollbars instance from the auto update loop. Only successful if the instance was added before.
         * @param instance The instance which shall be updated in a loop automatically.
         */
        _base.remove = function (instance) {
          var index = _inArray(instance, _loopingInstances);
          if (index > -1) {
            //remove from loopingInstances list
            _loopingInstancesIntervalCache.splice(index, 1);
            _loopingInstances.splice(index, 1);

            //correct update loop behavior
            if (_loopingInstances[_strLength] === 0 && _loopIsActive) {
              _loopIsActive = false;
              globals.autoUpdateLoop = _loopIsActive;
              if (_loopID !== undefined) {
                COMPATIBILITY.cAF()(_loopID);
                _loopID = -1;
              }
            }
          }
        };
      }

      /**
       * A object which manages the scrollbars visibility of the target element.
       * @param pluginTargetElement The element from which the scrollbars shall be hidden.
       * @param options The custom options.
       * @param extensions The custom extensions.
       * @param globals
       * @param autoUpdateLoop
       * @returns {*}
       * @constructor
       */
      function OverlayScrollbarsInstance(
        pluginTargetElement,
        options,
        extensions,
        globals,
        autoUpdateLoop
      ) {
        //shortcuts
        var type = COMPATIBILITY.type;
        var inArray = FRAMEWORK.inArray;
        var each = FRAMEWORK.each;

        //make correct instanceof
        var _base = new _plugin();
        var _frameworkProto = FRAMEWORK[LEXICON.p];

        //if passed element is no HTML element: skip and return
        if (!isHTMLElement(pluginTargetElement)) return;

        //if passed element is already initialized: set passed options if there are any and return its instance
        if (INSTANCES(pluginTargetElement)) {
          var inst = INSTANCES(pluginTargetElement);
          inst.options(options);
          return inst;
        }

        //globals:
        var _nativeScrollbarIsOverlaid;
        var _overlayScrollbarDummySize;
        var _rtlScrollBehavior;
        var _autoUpdateRecommended;
        var _msieVersion;
        var _nativeScrollbarStyling;
        var _cssCalc;
        var _nativeScrollbarSize;
        var _supportTransition;
        var _supportTransform;
        var _supportPassiveEvents;
        var _supportResizeObserver;
        var _supportMutationObserver;
        var _restrictedMeasuring;

        //general readonly:
        var _initialized;
        var _destroyed;
        var _isTextarea;
        var _isBody;
        var _documentMixed;
        var _domExists;

        //general:
        var _isBorderBox;
        var _sizeAutoObserverAdded;
        var _paddingX;
        var _paddingY;
        var _borderX;
        var _borderY;
        var _marginX;
        var _marginY;
        var _isRTL;
        var _sleeping;
        var _contentBorderSize = {};
        var _scrollHorizontalInfo = {};
        var _scrollVerticalInfo = {};
        var _viewportSize = {};
        var _nativeScrollbarMinSize = {};

        //naming:
        var _strMinusHidden = "-hidden";
        var _strMarginMinus = "margin-";
        var _strPaddingMinus = "padding-";
        var _strBorderMinus = "border-";
        var _strTop = "top";
        var _strRight = "right";
        var _strBottom = "bottom";
        var _strLeft = "left";
        var _strMinMinus = "min-";
        var _strMaxMinus = "max-";
        var _strWidth = "width";
        var _strHeight = "height";
        var _strFloat = "float";
        var _strEmpty = "";
        var _strAuto = "auto";
        var _strSync = "sync";
        var _strScroll = "scroll";
        var _strHundredPercent = "100%";
        var _strX = "x";
        var _strY = "y";
        var _strDot = ".";
        var _strSpace = " ";
        var _strScrollbar = "scrollbar";
        var _strMinusHorizontal = "-horizontal";
        var _strMinusVertical = "-vertical";
        var _strScrollLeft = _strScroll + "Left";
        var _strScrollTop = _strScroll + "Top";
        var _strMouseTouchDownEvent = "mousedown touchstart";
        var _strMouseTouchUpEvent = "mouseup touchend touchcancel";
        var _strMouseTouchMoveEvent = "mousemove touchmove";
        var _strMouseEnter = "mouseenter";
        var _strMouseLeave = "mouseleave";
        var _strKeyDownEvent = "keydown";
        var _strKeyUpEvent = "keyup";
        var _strSelectStartEvent = "selectstart";
        var _strTransitionEndEvent =
          "transitionend webkitTransitionEnd oTransitionEnd";
        var _strResizeObserverProperty = "__overlayScrollbarsRO__";

        //class names:
        var _cassNamesPrefix = "os-";
        var _classNameHTMLElement = _cassNamesPrefix + "html";
        var _classNameHostElement = _cassNamesPrefix + "host";
        var _classNameHostElementForeign = _classNameHostElement + "-foreign";
        var _classNameHostTextareaElement = _classNameHostElement + "-textarea";
        var _classNameHostScrollbarHorizontalHidden =
          _classNameHostElement +
          "-" +
          _strScrollbar +
          _strMinusHorizontal +
          _strMinusHidden;
        var _classNameHostScrollbarVerticalHidden =
          _classNameHostElement +
          "-" +
          _strScrollbar +
          _strMinusVertical +
          _strMinusHidden;
        var _classNameHostTransition = _classNameHostElement + "-transition";
        var _classNameHostRTL = _classNameHostElement + "-rtl";
        var _classNameHostResizeDisabled =
          _classNameHostElement + "-resize-disabled";
        var _classNameHostScrolling = _classNameHostElement + "-scrolling";
        var _classNameHostOverflow = _classNameHostElement + "-overflow";
        var _classNameHostOverflow = _classNameHostElement + "-overflow";
        var _classNameHostOverflowX = _classNameHostOverflow + "-x";
        var _classNameHostOverflowY = _classNameHostOverflow + "-y";
        var _classNameTextareaElement = _cassNamesPrefix + "textarea";
        var _classNameTextareaCoverElement =
          _classNameTextareaElement + "-cover";
        var _classNamePaddingElement = _cassNamesPrefix + "padding";
        var _classNameViewportElement = _cassNamesPrefix + "viewport";
        var _classNameViewportNativeScrollbarsInvisible =
          _classNameViewportElement + "-native-scrollbars-invisible";
        var _classNameViewportNativeScrollbarsOverlaid =
          _classNameViewportElement + "-native-scrollbars-overlaid";
        var _classNameContentElement = _cassNamesPrefix + "content";
        var _classNameContentArrangeElement =
          _cassNamesPrefix + "content-arrange";
        var _classNameContentGlueElement = _cassNamesPrefix + "content-glue";
        var _classNameSizeAutoObserverElement =
          _cassNamesPrefix + "size-auto-observer";
        var _classNameResizeObserverElement =
          _cassNamesPrefix + "resize-observer";
        var _classNameResizeObserverItemElement =
          _cassNamesPrefix + "resize-observer-item";
        var _classNameResizeObserverItemFinalElement =
          _classNameResizeObserverItemElement + "-final";
        var _classNameTextInherit = _cassNamesPrefix + "text-inherit";
        var _classNameScrollbar = _cassNamesPrefix + _strScrollbar;
        var _classNameScrollbarTrack = _classNameScrollbar + "-track";
        var _classNameScrollbarTrackOff = _classNameScrollbarTrack + "-off";
        var _classNameScrollbarHandle = _classNameScrollbar + "-handle";
        var _classNameScrollbarHandleOff = _classNameScrollbarHandle + "-off";
        var _classNameScrollbarUnusable = _classNameScrollbar + "-unusable";
        var _classNameScrollbarAutoHidden =
          _classNameScrollbar + "-" + _strAuto + _strMinusHidden;
        var _classNameScrollbarCorner = _classNameScrollbar + "-corner";
        var _classNameScrollbarCornerResize =
          _classNameScrollbarCorner + "-resize";
        var _classNameScrollbarCornerResizeB =
          _classNameScrollbarCornerResize + "-both";
        var _classNameScrollbarCornerResizeH =
          _classNameScrollbarCornerResize + _strMinusHorizontal;
        var _classNameScrollbarCornerResizeV =
          _classNameScrollbarCornerResize + _strMinusVertical;
        var _classNameScrollbarHorizontal =
          _classNameScrollbar + _strMinusHorizontal;
        var _classNameScrollbarVertical =
          _classNameScrollbar + _strMinusVertical;
        var _classNameDragging = _cassNamesPrefix + "dragging";
        var _classNameThemeNone = _cassNamesPrefix + "theme-none";
        var _classNamesDynamicDestroy = [
          _classNameViewportNativeScrollbarsInvisible,
          _classNameViewportNativeScrollbarsOverlaid,
          _classNameScrollbarTrackOff,
          _classNameScrollbarHandleOff,
          _classNameScrollbarUnusable,
          _classNameScrollbarAutoHidden,
          _classNameScrollbarCornerResize,
          _classNameScrollbarCornerResizeB,
          _classNameScrollbarCornerResizeH,
          _classNameScrollbarCornerResizeV,
          _classNameDragging,
        ].join(_strSpace);

        //callbacks:
        var _callbacksInitQeueue = [];

        //attrs viewport shall inherit from target
        var _viewportAttrsFromTarget = [LEXICON.ti];

        //options:
        var _defaultOptions;
        var _currentOptions;
        var _currentPreparedOptions;

        //extensions:
        var _extensions = {};
        var _extensionsPrivateMethods = "added removed on contract";

        //update
        var _lastUpdateTime;
        var _swallowedUpdateHints = {};
        var _swallowedUpdateTimeout;
        var _swallowUpdateLag = 42;
        var _updateOnLoadEventName = "load";
        var _updateOnLoadElms = [];

        //DOM elements:
        var _windowElement;
        var _documentElement;
        var _htmlElement;
        var _bodyElement;
        var _targetElement; //the target element of this OverlayScrollbars object
        var _hostElement; //the host element of this OverlayScrollbars object -> may be the same as targetElement
        var _sizeAutoObserverElement; //observes size auto changes
        var _sizeObserverElement; //observes size and padding changes
        var _paddingElement; //manages the padding
        var _viewportElement; //is the viewport of our scrollbar model
        var _contentElement; //the element which holds the content
        var _contentArrangeElement; //is needed for correct sizing of the content element (only if native scrollbars are overlays)
        var _contentGlueElement; //has always the size of the content element
        var _textareaCoverElement; //only applied if target is a textarea element. Used for correct size calculation and for prevention of uncontrolled scrolling
        var _scrollbarCornerElement;
        var _scrollbarHorizontalElement;
        var _scrollbarHorizontalTrackElement;
        var _scrollbarHorizontalHandleElement;
        var _scrollbarVerticalElement;
        var _scrollbarVerticalTrackElement;
        var _scrollbarVerticalHandleElement;
        var _windowElementNative;
        var _documentElementNative;
        var _targetElementNative;
        var _hostElementNative;
        var _sizeAutoObserverElementNative;
        var _sizeObserverElementNative;
        var _paddingElementNative;
        var _viewportElementNative;
        var _contentElementNative;

        //Cache:
        var _hostSizeCache;
        var _contentScrollSizeCache;
        var _arrangeContentSizeCache;
        var _hasOverflowCache;
        var _hideOverflowCache;
        var _widthAutoCache;
        var _heightAutoCache;
        var _cssBoxSizingCache;
        var _cssPaddingCache;
        var _cssBorderCache;
        var _cssMarginCache;
        var _cssDirectionCache;
        var _cssDirectionDetectedCache;
        var _paddingAbsoluteCache;
        var _clipAlwaysCache;
        var _contentGlueSizeCache;
        var _overflowBehaviorCache;
        var _overflowAmountCache;
        var _ignoreOverlayScrollbarHidingCache;
        var _autoUpdateCache;
        var _sizeAutoCapableCache;
        var _contentElementScrollSizeChangeDetectedCache;
        var _hostElementSizeChangeDetectedCache;
        var _scrollbarsVisibilityCache;
        var _scrollbarsAutoHideCache;
        var _scrollbarsClickScrollingCache;
        var _scrollbarsDragScrollingCache;
        var _resizeCache;
        var _normalizeRTLCache;
        var _classNameCache;
        var _oldClassName;
        var _textareaAutoWrappingCache;
        var _textareaInfoCache;
        var _textareaSizeCache;
        var _textareaDynHeightCache;
        var _textareaDynWidthCache;
        var _bodyMinSizeCache;
        var _updateAutoCache = {};

        //MutationObserver:
        var _mutationObserverHost;
        var _mutationObserverContent;
        var _mutationObserverHostCallback;
        var _mutationObserverContentCallback;
        var _mutationObserversConnected;
        var _mutationObserverAttrsTextarea = ["wrap", "cols", "rows"];
        var _mutationObserverAttrsHost = [
          LEXICON.i,
          LEXICON.c,
          LEXICON.s,
          "open",
        ].concat(_viewportAttrsFromTarget);

        //events:
        var _destroyEvents = [];

        //textarea:
        var _textareaHasFocus;

        //scrollbars:
        var _scrollbarsAutoHideTimeoutId;
        var _scrollbarsAutoHideMoveTimeoutId;
        var _scrollbarsAutoHideDelay;
        var _scrollbarsAutoHideNever;
        var _scrollbarsAutoHideScroll;
        var _scrollbarsAutoHideMove;
        var _scrollbarsAutoHideLeave;
        var _scrollbarsHandleHovered;
        var _scrollbarsHandlesDefineScrollPos;

        //resize
        var _resizeNone;
        var _resizeBoth;
        var _resizeHorizontal;
        var _resizeVertical;

        //==== Event Listener ====//

        /**
         * Adds or removes a event listener from the given element.
         * @param element The element to which the event listener shall be applied or removed.
         * @param eventNames The name(s) of the events.
         * @param listener The method which shall be called.
         * @param remove True if the handler shall be removed, false or undefined if the handler shall be added.
         * @param passiveOrOptions The options for the event.
         */
        function setupResponsiveEventListener(
          element,
          eventNames,
          listener,
          remove,
          passiveOrOptions
        ) {
          var collected =
            COMPATIBILITY.isA(eventNames) && COMPATIBILITY.isA(listener);
          var method = remove ? "removeEventListener" : "addEventListener";
          var onOff = remove ? "off" : "on";
          var events = collected ? false : eventNames.split(_strSpace);
          var i = 0;

          var passiveOrOptionsIsObj = FRAMEWORK.isPlainObject(passiveOrOptions);
          var passive =
            (_supportPassiveEvents &&
              (passiveOrOptionsIsObj
                ? passiveOrOptions._passive
                : passiveOrOptions)) ||
            false;
          var capture =
            passiveOrOptionsIsObj && (passiveOrOptions._capture || false);
          var nativeParam = _supportPassiveEvents
            ? {
                passive: passive,
                capture: capture,
              }
            : capture;

          if (collected) {
            for (; i < eventNames[LEXICON.l]; i++)
              setupResponsiveEventListener(
                element,
                eventNames[i],
                listener[i],
                remove,
                passiveOrOptions
              );
          } else {
            for (; i < events[LEXICON.l]; i++) {
              if (_supportPassiveEvents) {
                element[0][method](events[i], listener, nativeParam);
              } else {
                element[onOff](events[i], listener);
              }
            }
          }
        }

        function addDestroyEventListener(
          element,
          eventNames,
          listener,
          passive
        ) {
          setupResponsiveEventListener(
            element,
            eventNames,
            listener,
            false,
            passive
          );
          _destroyEvents.push(
            COMPATIBILITY.bind(
              setupResponsiveEventListener,
              0,
              element,
              eventNames,
              listener,
              true,
              passive
            )
          );
        }

        //==== Resize Observer ====//

        /**
         * Adds or removes a resize observer from the given element.
         * @param targetElement The element to which the resize observer shall be added or removed.
         * @param onElementResizedCallback The callback which is fired every time the resize observer registers a size change or false / undefined if the resizeObserver shall be removed.
         */
        function setupResizeObserver(targetElement, onElementResizedCallback) {
          if (targetElement) {
            var resizeObserver = COMPATIBILITY.rO();
            var strAnimationStartEvent =
              "animationstart mozAnimationStart webkitAnimationStart MSAnimationStart";
            var strChildNodes = "childNodes";
            var constScroll = 3333333;
            var callback = function () {
              targetElement[_strScrollTop](constScroll)[_strScrollLeft](
                _isRTL
                  ? _rtlScrollBehavior.n
                    ? -constScroll
                    : _rtlScrollBehavior.i
                    ? 0
                    : constScroll
                  : constScroll
              );
              onElementResizedCallback();
            };
            //add resize observer:
            if (onElementResizedCallback) {
              if (_supportResizeObserver) {
                var element = targetElement
                  .addClass("observed")
                  .append(generateDiv(_classNameResizeObserverElement))
                  .contents()[0];
                var observer = (element[_strResizeObserverProperty] =
                  new resizeObserver(callback));
                observer.observe(element);
              } else {
                if (_msieVersion > 9 || !_autoUpdateRecommended) {
                  targetElement.prepend(
                    generateDiv(
                      _classNameResizeObserverElement,
                      generateDiv(
                        { c: _classNameResizeObserverItemElement, dir: "ltr" },
                        generateDiv(
                          _classNameResizeObserverItemElement,
                          generateDiv(_classNameResizeObserverItemFinalElement)
                        ) +
                          generateDiv(
                            _classNameResizeObserverItemElement,
                            generateDiv({
                              c: _classNameResizeObserverItemFinalElement,
                              style: "width: 200%; height: 200%",
                            })
                          )
                      )
                    )
                  );

                  var observerElement =
                    targetElement[0][strChildNodes][0][strChildNodes][0];
                  var shrinkElement = FRAMEWORK(
                    observerElement[strChildNodes][1]
                  );
                  var expandElement = FRAMEWORK(
                    observerElement[strChildNodes][0]
                  );
                  var expandElementChild = FRAMEWORK(
                    expandElement[0][strChildNodes][0]
                  );
                  var widthCache = observerElement[LEXICON.oW];
                  var heightCache = observerElement[LEXICON.oH];
                  var isDirty;
                  var rAFId;
                  var currWidth;
                  var currHeight;
                  var factor = 2;
                  var nativeScrollbarSize = globals.nativeScrollbarSize; //care don't make changes to this object!!!
                  var reset = function () {
                    /*
                                         var sizeResetWidth = observerElement[LEXICON.oW] + nativeScrollbarSize.x * factor + nativeScrollbarSize.y * factor + _overlayScrollbarDummySize.x + _overlayScrollbarDummySize.y;
                                         var sizeResetHeight = observerElement[LEXICON.oH] + nativeScrollbarSize.x * factor + nativeScrollbarSize.y * factor + _overlayScrollbarDummySize.x + _overlayScrollbarDummySize.y;
                                         var expandChildCSS = {};
                                         expandChildCSS[_strWidth] = sizeResetWidth;
                                         expandChildCSS[_strHeight] = sizeResetHeight;
                                         expandElementChild.css(expandChildCSS);


                                         expandElement[_strScrollLeft](sizeResetWidth)[_strScrollTop](sizeResetHeight);
                                         shrinkElement[_strScrollLeft](sizeResetWidth)[_strScrollTop](sizeResetHeight);
                                         */
                    expandElement[_strScrollLeft](constScroll)[_strScrollTop](
                      constScroll
                    );
                    shrinkElement[_strScrollLeft](constScroll)[_strScrollTop](
                      constScroll
                    );
                  };
                  var onResized = function () {
                    rAFId = 0;
                    if (!isDirty) return;

                    widthCache = currWidth;
                    heightCache = currHeight;
                    callback();
                  };
                  var onScroll = function (event) {
                    currWidth = observerElement[LEXICON.oW];
                    currHeight = observerElement[LEXICON.oH];
                    isDirty =
                      currWidth != widthCache || currHeight != heightCache;

                    if (event && isDirty && !rAFId) {
                      COMPATIBILITY.cAF()(rAFId);
                      rAFId = COMPATIBILITY.rAF()(onResized);
                    } else if (!event) onResized();

                    reset();
                    if (event) {
                      COMPATIBILITY.prvD(event);
                      COMPATIBILITY.stpP(event);
                    }
                    return false;
                  };
                  var expandChildCSS = {};
                  var observerElementCSS = {};

                  setTopRightBottomLeft(observerElementCSS, _strEmpty, [
                    -((nativeScrollbarSize.y + 1) * factor),
                    nativeScrollbarSize.x * -factor,
                    nativeScrollbarSize.y * -factor,
                    -((nativeScrollbarSize.x + 1) * factor),
                  ]);

                  FRAMEWORK(observerElement).css(observerElementCSS);
                  expandElement.on(_strScroll, onScroll);
                  shrinkElement.on(_strScroll, onScroll);
                  targetElement.on(strAnimationStartEvent, function () {
                    onScroll(false);
                  });
                  //lets assume that the divs will never be that large and a constant value is enough
                  expandChildCSS[_strWidth] = constScroll;
                  expandChildCSS[_strHeight] = constScroll;
                  expandElementChild.css(expandChildCSS);

                  reset();
                } else {
                  var attachEvent = _documentElementNative.attachEvent;
                  var isIE = _msieVersion !== undefined;
                  if (attachEvent) {
                    targetElement.prepend(
                      generateDiv(_classNameResizeObserverElement)
                    );
                    findFirst(
                      targetElement,
                      _strDot + _classNameResizeObserverElement
                    )[0].attachEvent("onresize", callback);
                  } else {
                    var obj = _documentElementNative.createElement(TYPES.o);
                    obj.setAttribute(LEXICON.ti, "-1");
                    obj.setAttribute(
                      LEXICON.c,
                      _classNameResizeObserverElement
                    );
                    obj.onload = function () {
                      var wnd = this.contentDocument.defaultView;
                      wnd.addEventListener("resize", callback);
                      wnd.document.documentElement.style.display = "none";
                    };
                    obj.type = "text/html";
                    if (isIE) targetElement.prepend(obj);
                    obj.data = "about:blank";
                    if (!isIE) targetElement.prepend(obj);
                    targetElement.on(strAnimationStartEvent, callback);
                  }
                }
              }

              if (targetElement[0] === _sizeObserverElementNative) {
                var directionChanged = function () {
                  var dir = _hostElement.css("direction");
                  var css = {};
                  var scrollLeftValue = 0;
                  var result = false;
                  if (dir !== _cssDirectionDetectedCache) {
                    if (dir === "ltr") {
                      css[_strLeft] = 0;
                      css[_strRight] = _strAuto;
                      scrollLeftValue = constScroll;
                    } else {
                      css[_strLeft] = _strAuto;
                      css[_strRight] = 0;
                      scrollLeftValue = _rtlScrollBehavior.n
                        ? -constScroll
                        : _rtlScrollBehavior.i
                        ? 0
                        : constScroll;
                    }
                    //execution order is important for IE!!!
                    _sizeObserverElement.children().eq(0).css(css);
                    _sizeObserverElement[_strScrollLeft](scrollLeftValue)[
                      _strScrollTop
                    ](constScroll);
                    _cssDirectionDetectedCache = dir;
                    result = true;
                  }
                  return result;
                };
                directionChanged();
                addDestroyEventListener(
                  targetElement,
                  _strScroll,
                  function (event) {
                    if (directionChanged()) update();
                    COMPATIBILITY.prvD(event);
                    COMPATIBILITY.stpP(event);
                    return false;
                  }
                );
              }
            }
            //remove resize observer:
            else {
              if (_supportResizeObserver) {
                var element = targetElement.contents()[0];
                var resizeObserverObj = element[_strResizeObserverProperty];
                if (resizeObserverObj) {
                  resizeObserverObj.disconnect();
                  delete element[_strResizeObserverProperty];
                }
              } else {
                remove(
                  targetElement
                    .children(_strDot + _classNameResizeObserverElement)
                    .eq(0)
                );
              }
            }
          }
        }

        /**
                 * Freezes or unfreezes the given resize observer.
                 * @param targetElement The element to which the target resize observer is applied.
                 * @param freeze True if the resize observer shall be frozen, false otherwise.
                 
                function freezeResizeObserver(targetElement, freeze) {
                    if (targetElement !== undefined) {
                        if(freeze) {
                            if (_supportResizeObserver) {
                                var element = targetElement.contents()[0];
                                element[_strResizeObserverProperty].unobserve(element);
                            }
                            else {
                                targetElement = targetElement.children(_strDot + _classNameResizeObserverElement).eq(0);
                                var w = targetElement.css(_strWidth);
                                var h = targetElement.css(_strHeight);
                                var css = {};
                                css[_strWidth] = w;
                                css[_strHeight] = h;
                                targetElement.css(css);
                            }
                        }
                        else {
                            if (_supportResizeObserver) {
                                var element = targetElement.contents()[0];
                                element[_strResizeObserverProperty].observe(element);
                            }
                            else {
                                var css = { };
                                css[_strHeight] = _strEmpty;
                                css[_strWidth] = _strEmpty;
                                targetElement.children(_strDot + _classNameResizeObserverElement).eq(0).css(css);
                            }
                        }
                    }
                }
                */

        //==== Mutation Observers ====//

        /**
         * Creates MutationObservers for the host and content Element if they are supported.
         */
        function createMutationObservers() {
          if (_supportMutationObserver) {
            var mutationObserverContentLag = 11;
            var mutationObserver = COMPATIBILITY.mO();
            var contentLastUpdate = COMPATIBILITY.now();
            var mutationTarget;
            var mutationAttrName;
            var mutationIsClass;
            var oldMutationVal;
            var newClassVal;
            var hostClassNameRegex;
            var contentTimeout;
            var now;
            var sizeAuto;
            var action;

            _mutationObserverHostCallback = function (mutations) {
              var doUpdate = false;
              var doUpdateForce = false;
              var mutation;
              var mutatedAttrs = [];

              if (_initialized && !_sleeping) {
                each(mutations, function () {
                  mutation = this;
                  mutationTarget = mutation.target;
                  mutationAttrName = mutation.attributeName;
                  mutationIsClass = mutationAttrName === LEXICON.c;
                  oldMutationVal = mutation.oldValue;
                  newClassVal = mutationTarget.className;

                  if (_domExists && mutationIsClass && !doUpdateForce) {
                    // if old class value contains _classNameHostElementForeign and new class value doesn't
                    if (
                      oldMutationVal.indexOf(_classNameHostElementForeign) >
                        -1 &&
                      newClassVal.indexOf(_classNameHostElementForeign) < 0
                    ) {
                      hostClassNameRegex = createHostClassNameRegExp(true);
                      _hostElementNative.className = newClassVal
                        .split(_strSpace)
                        .concat(
                          oldMutationVal
                            .split(_strSpace)
                            .filter(function (name) {
                              return name.match(hostClassNameRegex);
                            })
                        )
                        .join(_strSpace);
                      doUpdate = doUpdateForce = true;
                    }
                  }

                  if (!doUpdate) {
                    doUpdate = mutationIsClass
                      ? hostClassNamesChanged(oldMutationVal, newClassVal)
                      : mutationAttrName === LEXICON.s
                      ? oldMutationVal !== mutationTarget[LEXICON.s].cssText
                      : true;
                  }

                  mutatedAttrs.push(mutationAttrName);
                });

                updateViewportAttrsFromTarget(mutatedAttrs);

                if (doUpdate) _base.update(doUpdateForce || _strAuto);
              }
              return doUpdate;
            };
            _mutationObserverContentCallback = function (mutations) {
              var doUpdate = false;
              var mutation;

              if (_initialized && !_sleeping) {
                each(mutations, function () {
                  mutation = this;
                  doUpdate = isUnknownMutation(mutation);
                  return !doUpdate;
                });

                if (doUpdate) {
                  now = COMPATIBILITY.now();
                  sizeAuto = _heightAutoCache || _widthAutoCache;
                  action = function () {
                    if (!_destroyed) {
                      contentLastUpdate = now;

                      //if cols, rows or wrap attr was changed
                      if (_isTextarea) textareaUpdate();

                      if (sizeAuto) update();
                      else _base.update(_strAuto);
                    }
                  };
                  clearTimeout(contentTimeout);
                  if (
                    mutationObserverContentLag <= 0 ||
                    now - contentLastUpdate > mutationObserverContentLag ||
                    !sizeAuto
                  )
                    action();
                  else
                    contentTimeout = setTimeout(
                      action,
                      mutationObserverContentLag
                    );
                }
              }
              return doUpdate;
            };

            _mutationObserverHost = new mutationObserver(
              _mutationObserverHostCallback
            );
            _mutationObserverContent = new mutationObserver(
              _mutationObserverContentCallback
            );
          }
        }

        /**
         * Connects the MutationObservers if they are supported.
         */
        function connectMutationObservers() {
          if (_supportMutationObserver && !_mutationObserversConnected) {
            _mutationObserverHost.observe(_hostElementNative, {
              attributes: true,
              attributeOldValue: true,
              attributeFilter: _mutationObserverAttrsHost,
            });

            _mutationObserverContent.observe(
              _isTextarea ? _targetElementNative : _contentElementNative,
              {
                attributes: true,
                attributeOldValue: true,
                subtree: !_isTextarea,
                childList: !_isTextarea,
                characterData: !_isTextarea,
                attributeFilter: _isTextarea
                  ? _mutationObserverAttrsTextarea
                  : _mutationObserverAttrsHost,
              }
            );

            _mutationObserversConnected = true;
          }
        }

        /**
         * Disconnects the MutationObservers if they are supported.
         */
        function disconnectMutationObservers() {
          if (_supportMutationObserver && _mutationObserversConnected) {
            _mutationObserverHost.disconnect();
            _mutationObserverContent.disconnect();

            _mutationObserversConnected = false;
          }
        }

        //==== Events of elements ====//

        /**
         * This method gets called every time the host element gets resized. IMPORTANT: Padding changes are detected too!!
         * It refreshes the hostResizedEventArgs and the hostSizeResizeCache.
         * If there are any size changes, the update method gets called.
         */
        function hostOnResized() {
          if (!_sleeping) {
            var changed;
            var hostSize = {
              w: _sizeObserverElementNative[LEXICON.sW],
              h: _sizeObserverElementNative[LEXICON.sH],
            };

            changed = checkCache(hostSize, _hostElementSizeChangeDetectedCache);
            _hostElementSizeChangeDetectedCache = hostSize;
            if (changed) update({ _hostSizeChanged: true });
          }
        }

        /**
         * The mouse enter event of the host element. This event is only needed for the autoHide feature.
         */
        function hostOnMouseEnter() {
          if (_scrollbarsAutoHideLeave) refreshScrollbarsAutoHide(true);
        }

        /**
         * The mouse leave event of the host element. This event is only needed for the autoHide feature.
         */
        function hostOnMouseLeave() {
          if (
            _scrollbarsAutoHideLeave &&
            !_bodyElement.hasClass(_classNameDragging)
          )
            refreshScrollbarsAutoHide(false);
        }

        /**
         * The mouse move event of the host element. This event is only needed for the autoHide "move" feature.
         */
        function hostOnMouseMove() {
          if (_scrollbarsAutoHideMove) {
            refreshScrollbarsAutoHide(true);
            clearTimeout(_scrollbarsAutoHideMoveTimeoutId);
            _scrollbarsAutoHideMoveTimeoutId = setTimeout(function () {
              if (_scrollbarsAutoHideMove && !_destroyed)
                refreshScrollbarsAutoHide(false);
            }, 100);
          }
        }

        /**
         * Prevents text from deselection if attached to the document element on the mousedown event of a DOM element.
         * @param event The select start event.
         */
        function documentOnSelectStart(event) {
          COMPATIBILITY.prvD(event);
          return false;
        }

        /**
         * A callback which will be called after a element has loaded.
         */
        function updateOnLoadCallback(event) {
          if (!_destroyed) {
            var target = event.target;
            var elm = FRAMEWORK(event.target);
            var index = FRAMEWORK.inArray(target, _updateOnLoadElms);
            if (index > -1) {
              _updateOnLoadElms.splice(index, 1);
            }

            eachUpdateOnLoad(function (i, updateOnLoadSelector) {
              if (elm.is(updateOnLoadSelector)) {
                update({ _contentSizeChanged: true });
              }
            });
          }
        }

        /**
         * Adds or removes mouse & touch events of the host element. (for handling auto-hiding of the scrollbars)
         * @param destroy Indicates whether the events shall be added or removed.
         */
        function setupHostMouseTouchEvents(destroy) {
          if (!destroy) setupHostMouseTouchEvents(true);

          setupResponsiveEventListener(
            _hostElement,
            _strMouseTouchMoveEvent.split(_strSpace)[0],
            hostOnMouseMove,
            !_scrollbarsAutoHideMove || destroy,
            true
          );
          setupResponsiveEventListener(
            _hostElement,
            [_strMouseEnter, _strMouseLeave],
            [hostOnMouseEnter, hostOnMouseLeave],
            !_scrollbarsAutoHideLeave || destroy,
            true
          );

          //if the plugin is initialized and the mouse is over the host element, make the scrollbars visible
          if (!_initialized && !destroy)
            _hostElement.one("mouseover", hostOnMouseEnter);
        }

        //==== Update Detection ====//

        /**
         * Measures the min width and min height of the body element and refreshes the related cache.
         * @returns {boolean} True if the min width or min height has changed, false otherwise.
         */
        function bodyMinSizeChanged() {
          var bodyMinSize = {};
          if (_isBody && _contentArrangeElement) {
            bodyMinSize.w = parseToZeroOrNumber(
              _contentArrangeElement.css(_strMinMinus + _strWidth)
            );
            bodyMinSize.h = parseToZeroOrNumber(
              _contentArrangeElement.css(_strMinMinus + _strHeight)
            );
            bodyMinSize.c = checkCache(bodyMinSize, _bodyMinSizeCache);
            bodyMinSize.f = true; //flag for "measured at least once"
          }
          _bodyMinSizeCache = bodyMinSize;
          return !!bodyMinSize.c;
        }

        /**
         * Returns true if the class names really changed (new class without plugin host prefix)
         * @param oldClassNames The old ClassName string or array.
         * @param newClassNames The new ClassName string or array.
         * @returns {boolean} True if the class names has really changed, false otherwise.
         */
        function hostClassNamesChanged(oldClassNames, newClassNames) {
          var currClasses =
            typeof newClassNames == TYPES.s
              ? newClassNames.split(_strSpace)
              : [];
          var oldClasses =
            typeof oldClassNames == TYPES.s
              ? oldClassNames.split(_strSpace)
              : [];
          var diff = getArrayDifferences(oldClasses, currClasses);

          // remove none theme from diff list to prevent update
          var idx = inArray(_classNameThemeNone, diff);
          var i;
          var regex;

          if (idx > -1) diff.splice(idx, 1);

          if (diff[LEXICON.l] > 0) {
            regex = createHostClassNameRegExp(true, true);
            for (i = 0; i < diff.length; i++) {
              if (!diff[i].match(regex)) {
                return true;
              }
            }
          }
          return false;
        }

        /**
         * Returns true if the given mutation is not from a from the plugin generated element. If the target element is a textarea the mutation is always unknown.
         * @param mutation The mutation which shall be checked.
         * @returns {boolean} True if the mutation is from a unknown element, false otherwise.
         */
        function isUnknownMutation(mutation) {
          var attributeName = mutation.attributeName;
          var mutationTarget = mutation.target;
          var mutationType = mutation.type;
          var strClosest = "closest";

          if (mutationTarget === _contentElementNative)
            return attributeName === null;
          if (
            mutationType === "attributes" &&
            (attributeName === LEXICON.c || attributeName === LEXICON.s) &&
            !_isTextarea
          ) {
            //ignore className changes by the plugin
            if (
              attributeName === LEXICON.c &&
              FRAMEWORK(mutationTarget).hasClass(_classNameHostElement)
            )
              return hostClassNamesChanged(
                mutation.oldValue,
                mutationTarget.className
              );

            //only do it of browser support it natively
            if (typeof mutationTarget[strClosest] != TYPES.f) return true;
            if (
              mutationTarget[strClosest](
                _strDot + _classNameResizeObserverElement
              ) !== null ||
              mutationTarget[strClosest](_strDot + _classNameScrollbar) !==
                null ||
              mutationTarget[strClosest](
                _strDot + _classNameScrollbarCorner
              ) !== null
            )
              return false;
          }
          return true;
        }

        /**
         * Returns true if the content size was changed since the last time this method was called.
         * @returns {boolean} True if the content size was changed, false otherwise.
         */
        function updateAutoContentSizeChanged() {
          if (_sleeping) return false;

          var contentMeasureElement = getContentMeasureElement();
          var textareaValueLength =
            _isTextarea && _widthAutoCache && !_textareaAutoWrappingCache
              ? _targetElement.val().length
              : 0;
          var setCSS =
            !_mutationObserversConnected && _widthAutoCache && !_isTextarea;
          var css = {};
          var float;
          var bodyMinSizeC;
          var changed;
          var contentElementScrollSize;

          if (setCSS) {
            float = _contentElement.css(_strFloat);
            css[_strFloat] = _isRTL ? _strRight : _strLeft;
            css[_strWidth] = _strAuto;
            _contentElement.css(css);
          }
          contentElementScrollSize = {
            w: contentMeasureElement[LEXICON.sW] + textareaValueLength,
            h: contentMeasureElement[LEXICON.sH] + textareaValueLength,
          };
          if (setCSS) {
            css[_strFloat] = float;
            css[_strWidth] = _strHundredPercent;
            _contentElement.css(css);
          }

          bodyMinSizeC = bodyMinSizeChanged();
          changed = checkCache(
            contentElementScrollSize,
            _contentElementScrollSizeChangeDetectedCache
          );

          _contentElementScrollSizeChangeDetectedCache =
            contentElementScrollSize;

          return changed || bodyMinSizeC;
        }

        /**
         * Returns true when a attribute which the MutationObserver would observe has changed.
         * @returns {boolean} True if one of the attributes which a MutationObserver would observe has changed, false or undefined otherwise.
         */
        function meaningfulAttrsChanged() {
          if (_sleeping || _mutationObserversConnected) return;

          var elem;
          var curr;
          var cache;
          var changedAttrs = [];
          var checks = [
            {
              _elem: _hostElement,
              _attrs: _mutationObserverAttrsHost.concat(":visible"),
            },
            {
              _elem: _isTextarea ? _targetElement : undefined,
              _attrs: _mutationObserverAttrsTextarea,
            },
          ];

          each(checks, function (index, check) {
            elem = check._elem;
            if (elem) {
              each(check._attrs, function (index, attr) {
                curr = attr.charAt(0) === ":" ? elem.is(attr) : elem.attr(attr);
                cache = _updateAutoCache[attr];

                if (checkCache(curr, cache)) {
                  changedAttrs.push(attr);
                }

                _updateAutoCache[attr] = curr;
              });
            }
          });

          updateViewportAttrsFromTarget(changedAttrs);

          return changedAttrs[LEXICON.l] > 0;
        }

        /**
         * Checks is a CSS Property of a child element is affecting the scroll size of the content.
         * @param propertyName The CSS property name.
         * @returns {boolean} True if the property is affecting the content scroll size, false otherwise.
         */
        function isSizeAffectingCSSProperty(propertyName) {
          if (!_initialized) return true;
          var flexGrow = "flex-grow";
          var flexShrink = "flex-shrink";
          var flexBasis = "flex-basis";
          var affectingPropsX = [
            _strWidth,
            _strMinMinus + _strWidth,
            _strMaxMinus + _strWidth,
            _strMarginMinus + _strLeft,
            _strMarginMinus + _strRight,
            _strLeft,
            _strRight,
            "font-weight",
            "word-spacing",
            flexGrow,
            flexShrink,
            flexBasis,
          ];
          var affectingPropsXContentBox = [
            _strPaddingMinus + _strLeft,
            _strPaddingMinus + _strRight,
            _strBorderMinus + _strLeft + _strWidth,
            _strBorderMinus + _strRight + _strWidth,
          ];
          var affectingPropsY = [
            _strHeight,
            _strMinMinus + _strHeight,
            _strMaxMinus + _strHeight,
            _strMarginMinus + _strTop,
            _strMarginMinus + _strBottom,
            _strTop,
            _strBottom,
            "line-height",
            flexGrow,
            flexShrink,
            flexBasis,
          ];
          var affectingPropsYContentBox = [
            _strPaddingMinus + _strTop,
            _strPaddingMinus + _strBottom,
            _strBorderMinus + _strTop + _strWidth,
            _strBorderMinus + _strBottom + _strWidth,
          ];
          var _strS = "s";
          var _strVS = "v-s";
          var checkX =
            _overflowBehaviorCache.x === _strS ||
            _overflowBehaviorCache.x === _strVS;
          var checkY =
            _overflowBehaviorCache.y === _strS ||
            _overflowBehaviorCache.y === _strVS;
          var sizeIsAffected = false;
          var checkPropertyName = function (arr, name) {
            for (var i = 0; i < arr[LEXICON.l]; i++) {
              if (arr[i] === name) return true;
            }
            return false;
          };

          if (checkY) {
            sizeIsAffected = checkPropertyName(affectingPropsY, propertyName);
            if (!sizeIsAffected && !_isBorderBox)
              sizeIsAffected = checkPropertyName(
                affectingPropsYContentBox,
                propertyName
              );
          }
          if (checkX && !sizeIsAffected) {
            sizeIsAffected = checkPropertyName(affectingPropsX, propertyName);
            if (!sizeIsAffected && !_isBorderBox)
              sizeIsAffected = checkPropertyName(
                affectingPropsXContentBox,
                propertyName
              );
          }
          return sizeIsAffected;
        }

        //==== Update ====//

        /**
         * Sets the attribute values of the viewport element to the values from the target element.
         * The value of a attribute is only set if the attribute is whitelisted.
         * @attrs attrs The array of attributes which shall be set or undefined if all whitelisted shall be set.
         */
        function updateViewportAttrsFromTarget(attrs) {
          attrs = attrs || _viewportAttrsFromTarget;
          each(attrs, function (index, attr) {
            if (COMPATIBILITY.inA(attr, _viewportAttrsFromTarget) > -1) {
              var targetAttr = _targetElement.attr(attr);
              if (type(targetAttr) == TYPES.s) {
                _viewportElement.attr(attr, targetAttr);
              } else {
                _viewportElement.removeAttr(attr);
              }
            }
          });
        }

        /**
         * Updates the variables and size of the textarea element, and manages the scroll on new line or new character.
         */
        function textareaUpdate() {
          if (!_sleeping) {
            var wrapAttrOff = !_textareaAutoWrappingCache;
            var minWidth = _viewportSize.w;
            var minHeight = _viewportSize.h;
            var css = {};
            var doMeasure = _widthAutoCache || wrapAttrOff;
            var origWidth;
            var width;
            var origHeight;
            var height;

            //reset min size
            css[_strMinMinus + _strWidth] = _strEmpty;
            css[_strMinMinus + _strHeight] = _strEmpty;

            //set width auto
            css[_strWidth] = _strAuto;
            _targetElement.css(css);

            //measure width
            origWidth = _targetElementNative[LEXICON.oW];
            width = doMeasure
              ? MATH.max(origWidth, _targetElementNative[LEXICON.sW] - 1)
              : 1;
            /*width += (_widthAutoCache ? _marginX + (!_isBorderBox ? wrapAttrOff ? 0 : _paddingX + _borderX : 0) : 0);*/

            //set measured width
            css[_strWidth] = _widthAutoCache
              ? _strAuto /*width*/
              : _strHundredPercent;
            css[_strMinMinus + _strWidth] = _strHundredPercent;

            //set height auto
            css[_strHeight] = _strAuto;
            _targetElement.css(css);

            //measure height
            origHeight = _targetElementNative[LEXICON.oH];
            height = MATH.max(origHeight, _targetElementNative[LEXICON.sH] - 1);

            //append correct size values
            css[_strWidth] = width;
            css[_strHeight] = height;
            _textareaCoverElement.css(css);

            //apply min width / min height to prevent textarea collapsing
            css[_strMinMinus + _strWidth] =
              minWidth /*+ (!_isBorderBox && _widthAutoCache ? _paddingX + _borderX : 0)*/;
            css[_strMinMinus + _strHeight] =
              minHeight /*+ (!_isBorderBox && _heightAutoCache ? _paddingY + _borderY : 0)*/;
            _targetElement.css(css);

            return {
              _originalWidth: origWidth,
              _originalHeight: origHeight,
              _dynamicWidth: width,
              _dynamicHeight: height,
            };
          }
        }

        /**
         * Updates the plugin and DOM to the current options.
         * This method should only be called if a update is 100% required.
         * @param updateHints A objects which contains hints for this update:
         * {
         *   _hostSizeChanged : boolean,
         *   _contentSizeChanged : boolean,
         *   _force : boolean,                             == preventSwallowing
         *   _changedOptions : { },                        == preventSwallowing && preventSleep
         *  }
         */
        function update(updateHints) {
          clearTimeout(_swallowedUpdateTimeout);
          updateHints = updateHints || {};
          _swallowedUpdateHints._hostSizeChanged |=
            updateHints._hostSizeChanged;
          _swallowedUpdateHints._contentSizeChanged |=
            updateHints._contentSizeChanged;
          _swallowedUpdateHints._force |= updateHints._force;

          var now = COMPATIBILITY.now();
          var hostSizeChanged = !!_swallowedUpdateHints._hostSizeChanged;
          var contentSizeChanged = !!_swallowedUpdateHints._contentSizeChanged;
          var force = !!_swallowedUpdateHints._force;
          var changedOptions = updateHints._changedOptions;
          var swallow =
            _swallowUpdateLag > 0 &&
            _initialized &&
            !_destroyed &&
            !force &&
            !changedOptions &&
            now - _lastUpdateTime < _swallowUpdateLag &&
            !_heightAutoCache &&
            !_widthAutoCache;
          var displayIsHidden;

          if (swallow)
            _swallowedUpdateTimeout = setTimeout(update, _swallowUpdateLag);

          //abort update due to:
          //destroyed
          //swallowing
          //sleeping
          //host is hidden or has false display
          if (
            _destroyed ||
            swallow ||
            (_sleeping && !changedOptions) ||
            (_initialized &&
              !force &&
              (displayIsHidden = _hostElement.is(":hidden"))) ||
            _hostElement.css("display") === "inline"
          )
            return;

          _lastUpdateTime = now;
          _swallowedUpdateHints = {};

          //if scrollbar styling is possible and native scrollbars aren't overlaid the scrollbar styling will be applied which hides the native scrollbars completely.
          if (
            _nativeScrollbarStyling &&
            !(_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)
          ) {
            //native scrollbars are hidden, so change the values to zero
            _nativeScrollbarSize.x = 0;
            _nativeScrollbarSize.y = 0;
          } else {
            //refresh native scrollbar size (in case of zoom)
            _nativeScrollbarSize = extendDeep({}, globals.nativeScrollbarSize);
          }

          // Scrollbar padding is needed for firefox, because firefox hides scrollbar automatically if the size of the div is too small.
          // The calculation: [scrollbar size +3 *3]
          // (+3 because of possible decoration e.g. borders, margins etc., but only if native scrollbar is NOT a overlaid scrollbar)
          // (*3 because (1)increase / (2)decrease -button and (3)resize handle)
          _nativeScrollbarMinSize = {
            x:
              (_nativeScrollbarSize.x +
                (_nativeScrollbarIsOverlaid.x ? 0 : 3)) *
              3,
            y:
              (_nativeScrollbarSize.y +
                (_nativeScrollbarIsOverlaid.y ? 0 : 3)) *
              3,
          };

          changedOptions = changedOptions || {};
          //freezeResizeObserver(_sizeObserverElement, true);
          //freezeResizeObserver(_sizeAutoObserverElement, true);

          var checkCacheAutoForce = function () {
            return checkCache.apply(
              this,
              [].slice.call(arguments).concat([force])
            );
          };

          //save current scroll offset
          var currScroll = {
            x: _viewportElement[_strScrollLeft](),
            y: _viewportElement[_strScrollTop](),
          };

          var currentPreparedOptionsScrollbars =
            _currentPreparedOptions.scrollbars;
          var currentPreparedOptionsTextarea = _currentPreparedOptions.textarea;

          //scrollbars visibility:
          var scrollbarsVisibility =
            currentPreparedOptionsScrollbars.visibility;
          var scrollbarsVisibilityChanged = checkCacheAutoForce(
            scrollbarsVisibility,
            _scrollbarsVisibilityCache
          );

          //scrollbars autoHide:
          var scrollbarsAutoHide = currentPreparedOptionsScrollbars.autoHide;
          var scrollbarsAutoHideChanged = checkCacheAutoForce(
            scrollbarsAutoHide,
            _scrollbarsAutoHideCache
          );

          //scrollbars click scrolling
          var scrollbarsClickScrolling =
            currentPreparedOptionsScrollbars.clickScrolling;
          var scrollbarsClickScrollingChanged = checkCacheAutoForce(
            scrollbarsClickScrolling,
            _scrollbarsClickScrollingCache
          );

          //scrollbars drag scrolling
          var scrollbarsDragScrolling =
            currentPreparedOptionsScrollbars.dragScrolling;
          var scrollbarsDragScrollingChanged = checkCacheAutoForce(
            scrollbarsDragScrolling,
            _scrollbarsDragScrollingCache
          );

          //className
          var className = _currentPreparedOptions.className;
          var classNameChanged = checkCacheAutoForce(
            className,
            _classNameCache
          );

          //resize
          var resize = _currentPreparedOptions.resize;
          var resizeChanged =
            checkCacheAutoForce(resize, _resizeCache) && !_isBody; //body can't be resized since the window itself acts as resize possibility.

          //paddingAbsolute
          var paddingAbsolute = _currentPreparedOptions.paddingAbsolute;
          var paddingAbsoluteChanged = checkCacheAutoForce(
            paddingAbsolute,
            _paddingAbsoluteCache
          );

          //clipAlways
          var clipAlways = _currentPreparedOptions.clipAlways;
          var clipAlwaysChanged = checkCacheAutoForce(
            clipAlways,
            _clipAlwaysCache
          );

          //sizeAutoCapable
          var sizeAutoCapable =
            _currentPreparedOptions.sizeAutoCapable && !_isBody; //body can never be size auto, because it shall be always as big as the viewport.
          var sizeAutoCapableChanged = checkCacheAutoForce(
            sizeAutoCapable,
            _sizeAutoCapableCache
          );

          //showNativeScrollbars
          var ignoreOverlayScrollbarHiding =
            _currentPreparedOptions.nativeScrollbarsOverlaid
              .showNativeScrollbars;
          var ignoreOverlayScrollbarHidingChanged = checkCacheAutoForce(
            ignoreOverlayScrollbarHiding,
            _ignoreOverlayScrollbarHidingCache
          );

          //autoUpdate
          var autoUpdate = _currentPreparedOptions.autoUpdate;
          var autoUpdateChanged = checkCacheAutoForce(
            autoUpdate,
            _autoUpdateCache
          );

          //overflowBehavior
          var overflowBehavior = _currentPreparedOptions.overflowBehavior;
          var overflowBehaviorChanged = checkCacheAutoForce(
            overflowBehavior,
            _overflowBehaviorCache,
            force
          );

          //dynWidth:
          var textareaDynWidth = currentPreparedOptionsTextarea.dynWidth;
          var textareaDynWidthChanged = checkCacheAutoForce(
            _textareaDynWidthCache,
            textareaDynWidth
          );

          //dynHeight:
          var textareaDynHeight = currentPreparedOptionsTextarea.dynHeight;
          var textareaDynHeightChanged = checkCacheAutoForce(
            _textareaDynHeightCache,
            textareaDynHeight
          );

          //scrollbars visibility
          _scrollbarsAutoHideNever = scrollbarsAutoHide === "n";
          _scrollbarsAutoHideScroll = scrollbarsAutoHide === "s";
          _scrollbarsAutoHideMove = scrollbarsAutoHide === "m";
          _scrollbarsAutoHideLeave = scrollbarsAutoHide === "l";

          //scrollbars autoHideDelay
          _scrollbarsAutoHideDelay =
            currentPreparedOptionsScrollbars.autoHideDelay;

          //old className
          _oldClassName = _classNameCache;

          //resize
          _resizeNone = resize === "n";
          _resizeBoth = resize === "b";
          _resizeHorizontal = resize === "h";
          _resizeVertical = resize === "v";

          //normalizeRTL
          _normalizeRTLCache = _currentPreparedOptions.normalizeRTL;

          //ignore overlay scrollbar hiding
          ignoreOverlayScrollbarHiding =
            ignoreOverlayScrollbarHiding &&
            _nativeScrollbarIsOverlaid.x &&
            _nativeScrollbarIsOverlaid.y;

          //refresh options cache
          _scrollbarsVisibilityCache = scrollbarsVisibility;
          _scrollbarsAutoHideCache = scrollbarsAutoHide;
          _scrollbarsClickScrollingCache = scrollbarsClickScrolling;
          _scrollbarsDragScrollingCache = scrollbarsDragScrolling;
          _classNameCache = className;
          _resizeCache = resize;
          _paddingAbsoluteCache = paddingAbsolute;
          _clipAlwaysCache = clipAlways;
          _sizeAutoCapableCache = sizeAutoCapable;
          _ignoreOverlayScrollbarHidingCache = ignoreOverlayScrollbarHiding;
          _autoUpdateCache = autoUpdate;
          _overflowBehaviorCache = extendDeep({}, overflowBehavior);
          _textareaDynWidthCache = textareaDynWidth;
          _textareaDynHeightCache = textareaDynHeight;
          _hasOverflowCache = _hasOverflowCache || { x: false, y: false };

          //set correct class name to the host element
          if (classNameChanged) {
            removeClass(
              _hostElement,
              _oldClassName + _strSpace + _classNameThemeNone
            );
            addClass(
              _hostElement,
              className !== undefined &&
                className !== null &&
                className.length > 0
                ? className
                : _classNameThemeNone
            );
          }

          //set correct auto Update
          if (autoUpdateChanged) {
            if (
              autoUpdate === true ||
              (autoUpdate === null && _autoUpdateRecommended)
            ) {
              disconnectMutationObservers();
              autoUpdateLoop.add(_base);
            } else {
              autoUpdateLoop.remove(_base);
              connectMutationObservers();
            }
          }

          //activate or deactivate size auto capability
          if (sizeAutoCapableChanged) {
            if (sizeAutoCapable) {
              if (_contentGlueElement) {
                _contentGlueElement.show();
              } else {
                _contentGlueElement = FRAMEWORK(
                  generateDiv(_classNameContentGlueElement)
                );
                _paddingElement.before(_contentGlueElement);
              }
              if (_sizeAutoObserverAdded) {
                _sizeAutoObserverElement.show();
              } else {
                _sizeAutoObserverElement = FRAMEWORK(
                  generateDiv(_classNameSizeAutoObserverElement)
                );
                _sizeAutoObserverElementNative = _sizeAutoObserverElement[0];

                _contentGlueElement.before(_sizeAutoObserverElement);
                var oldSize = { w: -1, h: -1 };
                setupResizeObserver(_sizeAutoObserverElement, function () {
                  var newSize = {
                    w: _sizeAutoObserverElementNative[LEXICON.oW],
                    h: _sizeAutoObserverElementNative[LEXICON.oH],
                  };
                  if (checkCache(newSize, oldSize)) {
                    if (
                      (_initialized && _heightAutoCache && newSize.h > 0) ||
                      (_widthAutoCache && newSize.w > 0)
                    ) {
                      update();
                    } else if (
                      (_initialized && !_heightAutoCache && newSize.h === 0) ||
                      (!_widthAutoCache && newSize.w === 0)
                    ) {
                      update();
                    }
                  }
                  oldSize = newSize;
                });
                _sizeAutoObserverAdded = true;
                //fix heightAuto detector bug if height is fixed but contentHeight is 0.
                //the probability this bug will ever happen is very very low, thats why its ok if we use calc which isn't supported in IE8.
                if (_cssCalc !== null)
                  _sizeAutoObserverElement.css(
                    _strHeight,
                    _cssCalc + "(100% + 1px)"
                  );
              }
            } else {
              if (_sizeAutoObserverAdded) _sizeAutoObserverElement.hide();
              if (_contentGlueElement) _contentGlueElement.hide();
            }
          }

          //if force, update all resizeObservers too
          if (force) {
            _sizeObserverElement.find("*").trigger(_strScroll);
            if (_sizeAutoObserverAdded)
              _sizeAutoObserverElement.find("*").trigger(_strScroll);
          }

          //display hidden:
          displayIsHidden =
            displayIsHidden === undefined
              ? _hostElement.is(":hidden")
              : displayIsHidden;

          //textarea AutoWrapping:
          var textareaAutoWrapping = _isTextarea
            ? _targetElement.attr("wrap") !== "off"
            : false;
          var textareaAutoWrappingChanged = checkCacheAutoForce(
            textareaAutoWrapping,
            _textareaAutoWrappingCache
          );

          //detect direction:
          var cssDirection = _hostElement.css("direction");
          var cssDirectionChanged = checkCacheAutoForce(
            cssDirection,
            _cssDirectionCache
          );

          //detect box-sizing:
          var boxSizing = _hostElement.css("box-sizing");
          var boxSizingChanged = checkCacheAutoForce(
            boxSizing,
            _cssBoxSizingCache
          );

          //detect padding:
          var padding = getTopRightBottomLeftHost(_strPaddingMinus);

          //width + height auto detecting var:
          var sizeAutoObserverElementBCRect;
          //exception occurs in IE8 sometimes (unknown exception)
          try {
            sizeAutoObserverElementBCRect = _sizeAutoObserverAdded
              ? _sizeAutoObserverElementNative[LEXICON.bCR]()
              : null;
          } catch (ex) {
            return;
          }

          _isRTL = cssDirection === "rtl";
          _isBorderBox = boxSizing === "border-box";
          var isRTLLeft = _isRTL ? _strLeft : _strRight;
          var isRTLRight = _isRTL ? _strRight : _strLeft;

          //detect width auto:
          var widthAutoResizeDetection = false;
          var widthAutoObserverDetection =
            _sizeAutoObserverAdded &&
            _hostElement.css(_strFloat) !== "none" /*|| _isTextarea */
              ? MATH.round(
                  sizeAutoObserverElementBCRect.right -
                    sizeAutoObserverElementBCRect.left
                ) === 0 &&
                (!paddingAbsolute
                  ? _hostElementNative[LEXICON.cW] - _paddingX > 0
                  : true)
              : false;
          if (sizeAutoCapable && !widthAutoObserverDetection) {
            var tmpCurrHostWidth = _hostElementNative[LEXICON.oW];
            var tmpCurrContentGlueWidth = _contentGlueElement.css(_strWidth);
            _contentGlueElement.css(_strWidth, _strAuto);

            var tmpNewHostWidth = _hostElementNative[LEXICON.oW];
            _contentGlueElement.css(_strWidth, tmpCurrContentGlueWidth);
            widthAutoResizeDetection = tmpCurrHostWidth !== tmpNewHostWidth;
            if (!widthAutoResizeDetection) {
              _contentGlueElement.css(_strWidth, tmpCurrHostWidth + 1);
              tmpNewHostWidth = _hostElementNative[LEXICON.oW];
              _contentGlueElement.css(_strWidth, tmpCurrContentGlueWidth);
              widthAutoResizeDetection = tmpCurrHostWidth !== tmpNewHostWidth;
            }
          }
          var widthAuto =
            (widthAutoObserverDetection || widthAutoResizeDetection) &&
            sizeAutoCapable &&
            !displayIsHidden;
          var widthAutoChanged = checkCacheAutoForce(
            widthAuto,
            _widthAutoCache
          );
          var wasWidthAuto = !widthAuto && _widthAutoCache;

          //detect height auto:
          var heightAuto =
            _sizeAutoObserverAdded && sizeAutoCapable && !displayIsHidden
              ? MATH.round(
                  sizeAutoObserverElementBCRect.bottom -
                    sizeAutoObserverElementBCRect.top
                ) ===
                0 /* && (!paddingAbsolute && (_msieVersion > 9 || !_msieVersion) ? true : true) */
              : false;
          var heightAutoChanged = checkCacheAutoForce(
            heightAuto,
            _heightAutoCache
          );
          var wasHeightAuto = !heightAuto && _heightAutoCache;

          //detect border:
          //we need the border only if border box and auto size
          var updateBorderX = (widthAuto && _isBorderBox) || !_isBorderBox;
          var updateBorderY = (heightAuto && _isBorderBox) || !_isBorderBox;
          var border = getTopRightBottomLeftHost(
            _strBorderMinus,
            "-" + _strWidth,
            !updateBorderX,
            !updateBorderY
          );

          //detect margin:
          var margin = getTopRightBottomLeftHost(_strMarginMinus);

          //vars to apply correct css
          var contentElementCSS = {};
          var contentGlueElementCSS = {};

          //funcs
          var getHostSize = function () {
            //has to be clientSize because offsetSize respect borders
            return {
              w: _hostElementNative[LEXICON.cW],
              h: _hostElementNative[LEXICON.cH],
            };
          };
          var getViewportSize = function () {
            //viewport size is padding container because it never has padding, margin and a border
            //determine zoom rounding error -> sometimes scrollWidth/Height is smaller than clientWidth/Height
            //if this happens add the difference to the viewportSize to compensate the rounding error
            return {
              w:
                _paddingElementNative[LEXICON.oW] +
                MATH.max(
                  0,
                  _contentElementNative[LEXICON.cW] -
                    _contentElementNative[LEXICON.sW]
                ),
              h:
                _paddingElementNative[LEXICON.oH] +
                MATH.max(
                  0,
                  _contentElementNative[LEXICON.cH] -
                    _contentElementNative[LEXICON.sH]
                ),
            };
          };

          //set info for padding
          var paddingAbsoluteX = (_paddingX = padding.l + padding.r);
          var paddingAbsoluteY = (_paddingY = padding.t + padding.b);
          paddingAbsoluteX *= paddingAbsolute ? 1 : 0;
          paddingAbsoluteY *= paddingAbsolute ? 1 : 0;
          padding.c = checkCacheAutoForce(padding, _cssPaddingCache);

          //set info for border
          _borderX = border.l + border.r;
          _borderY = border.t + border.b;
          border.c = checkCacheAutoForce(border, _cssBorderCache);

          //set info for margin
          _marginX = margin.l + margin.r;
          _marginY = margin.t + margin.b;
          margin.c = checkCacheAutoForce(margin, _cssMarginCache);

          //refresh cache
          _textareaAutoWrappingCache = textareaAutoWrapping;
          _cssDirectionCache = cssDirection;
          _cssBoxSizingCache = boxSizing;
          _widthAutoCache = widthAuto;
          _heightAutoCache = heightAuto;
          _cssPaddingCache = padding;
          _cssBorderCache = border;
          _cssMarginCache = margin;

          //IEFix direction changed
          if (cssDirectionChanged && _sizeAutoObserverAdded)
            _sizeAutoObserverElement.css(_strFloat, isRTLRight);

          //apply padding:
          if (
            padding.c ||
            cssDirectionChanged ||
            paddingAbsoluteChanged ||
            widthAutoChanged ||
            heightAutoChanged ||
            boxSizingChanged ||
            sizeAutoCapableChanged
          ) {
            var paddingElementCSS = {};
            var textareaCSS = {};
            var paddingValues = [padding.t, padding.r, padding.b, padding.l];

            setTopRightBottomLeft(contentGlueElementCSS, _strMarginMinus, [
              -padding.t,
              -padding.r,
              -padding.b,
              -padding.l,
            ]);
            if (paddingAbsolute) {
              setTopRightBottomLeft(
                paddingElementCSS,
                _strEmpty,
                paddingValues
              );
              setTopRightBottomLeft(
                _isTextarea ? textareaCSS : contentElementCSS,
                _strPaddingMinus
              );
            } else {
              setTopRightBottomLeft(paddingElementCSS, _strEmpty);
              setTopRightBottomLeft(
                _isTextarea ? textareaCSS : contentElementCSS,
                _strPaddingMinus,
                paddingValues
              );
            }

            _paddingElement.css(paddingElementCSS);
            _targetElement.css(textareaCSS);
          }

          //viewport size is padding container because it never has padding, margin and a border.
          _viewportSize = getViewportSize();

          //update Textarea
          var textareaSize = _isTextarea ? textareaUpdate() : false;
          var textareaSizeChanged =
            _isTextarea &&
            checkCacheAutoForce(textareaSize, _textareaSizeCache);
          var textareaDynOrigSize =
            _isTextarea && textareaSize
              ? {
                  w: textareaDynWidth
                    ? textareaSize._dynamicWidth
                    : textareaSize._originalWidth,
                  h: textareaDynHeight
                    ? textareaSize._dynamicHeight
                    : textareaSize._originalHeight,
                }
              : {};
          _textareaSizeCache = textareaSize;

          //fix height auto / width auto in cooperation with current padding & boxSizing behavior:
          if (
            heightAuto &&
            (heightAutoChanged ||
              paddingAbsoluteChanged ||
              boxSizingChanged ||
              padding.c ||
              border.c)
          ) {
            contentElementCSS[_strHeight] = _strAuto;
          } else if (heightAutoChanged || paddingAbsoluteChanged) {
            contentElementCSS[_strHeight] = _strHundredPercent;
          }
          if (
            widthAuto &&
            (widthAutoChanged ||
              paddingAbsoluteChanged ||
              boxSizingChanged ||
              padding.c ||
              border.c ||
              cssDirectionChanged)
          ) {
            contentElementCSS[_strWidth] = _strAuto;
            contentGlueElementCSS[_strMaxMinus + _strWidth] =
              _strHundredPercent; //IE Fix
          } else if (widthAutoChanged || paddingAbsoluteChanged) {
            contentElementCSS[_strWidth] = _strHundredPercent;
            contentElementCSS[_strFloat] = _strEmpty;
            contentGlueElementCSS[_strMaxMinus + _strWidth] = _strEmpty; //IE Fix
          }
          if (widthAuto) {
            //textareaDynOrigSize.w || _strAuto :: doesnt works because applied margin will shift width
            contentGlueElementCSS[_strWidth] = _strAuto;

            contentElementCSS[_strWidth] =
              VENDORS._cssPropertyValue(_strWidth, "max-content intrinsic") ||
              _strAuto;
            contentElementCSS[_strFloat] = isRTLRight;
          } else {
            contentGlueElementCSS[_strWidth] = _strEmpty;
          }
          if (heightAuto) {
            //textareaDynOrigSize.h || _contentElementNative[LEXICON.cH] :: use for anti scroll jumping
            contentGlueElementCSS[_strHeight] =
              textareaDynOrigSize.h || _contentElementNative[LEXICON.cH];
          } else {
            contentGlueElementCSS[_strHeight] = _strEmpty;
          }
          if (sizeAutoCapable) _contentGlueElement.css(contentGlueElementCSS);
          _contentElement.css(contentElementCSS);

          //CHECKPOINT HERE ~
          contentElementCSS = {};
          contentGlueElementCSS = {};

          //if [content(host) client / scroll size, or target element direction, or content(host) max-sizes] changed, or force is true
          if (
            hostSizeChanged ||
            contentSizeChanged ||
            textareaSizeChanged ||
            cssDirectionChanged ||
            boxSizingChanged ||
            paddingAbsoluteChanged ||
            widthAutoChanged ||
            widthAuto ||
            heightAutoChanged ||
            heightAuto ||
            ignoreOverlayScrollbarHidingChanged ||
            overflowBehaviorChanged ||
            clipAlwaysChanged ||
            resizeChanged ||
            scrollbarsVisibilityChanged ||
            scrollbarsAutoHideChanged ||
            scrollbarsDragScrollingChanged ||
            scrollbarsClickScrollingChanged ||
            textareaDynWidthChanged ||
            textareaDynHeightChanged ||
            textareaAutoWrappingChanged
          ) {
            var strOverflow = "overflow";
            var strOverflowX = strOverflow + "-x";
            var strOverflowY = strOverflow + "-y";
            var strHidden = "hidden";
            var strVisible = "visible";

            //Reset the viewport (very important for natively overlaid scrollbars and zoom change
            //don't change the overflow prop as it is very expensive and affects performance !A LOT!
            if (!_nativeScrollbarStyling) {
              var viewportElementResetCSS = {};
              var resetXTmp =
                _hasOverflowCache.y &&
                _hideOverflowCache.ys &&
                !ignoreOverlayScrollbarHiding
                  ? _nativeScrollbarIsOverlaid.y
                    ? _viewportElement.css(isRTLLeft)
                    : -_nativeScrollbarSize.y
                  : 0;
              var resetBottomTmp =
                _hasOverflowCache.x &&
                _hideOverflowCache.xs &&
                !ignoreOverlayScrollbarHiding
                  ? _nativeScrollbarIsOverlaid.x
                    ? _viewportElement.css(_strBottom)
                    : -_nativeScrollbarSize.x
                  : 0;
              setTopRightBottomLeft(viewportElementResetCSS, _strEmpty);
              _viewportElement.css(viewportElementResetCSS);
            }

            //measure several sizes:
            var contentMeasureElement = getContentMeasureElement();
            //in Firefox content element has to have overflow hidden, else element margins aren't calculated properly, this element prevents this bug, but only if scrollbars aren't overlaid
            var contentSize = {
              //use clientSize because natively overlaidScrollbars add borders
              w: textareaDynOrigSize.w || contentMeasureElement[LEXICON.cW],
              h: textareaDynOrigSize.h || contentMeasureElement[LEXICON.cH],
            };
            var scrollSize = {
              w: contentMeasureElement[LEXICON.sW],
              h: contentMeasureElement[LEXICON.sH],
            };

            //apply the correct viewport style and measure viewport size
            if (!_nativeScrollbarStyling) {
              viewportElementResetCSS[_strBottom] = wasHeightAuto
                ? _strEmpty
                : resetBottomTmp;
              viewportElementResetCSS[isRTLLeft] = wasWidthAuto
                ? _strEmpty
                : resetXTmp;
              _viewportElement.css(viewportElementResetCSS);
            }
            _viewportSize = getViewportSize();

            //measure and correct several sizes
            var hostSize = getHostSize();
            var hostAbsoluteRectSize = {
              w:
                hostSize.w -
                _marginX -
                _borderX -
                (_isBorderBox ? 0 : _paddingX),
              h:
                hostSize.h -
                _marginY -
                _borderY -
                (_isBorderBox ? 0 : _paddingY),
            };
            var contentGlueSize = {
              //client/scrollSize + AbsolutePadding -> because padding is only applied to the paddingElement if its absolute, so you have to add it manually
              //hostSize is clientSize -> so padding should be added manually, right? FALSE! Because content glue is inside hostElement, so we don't have to worry about padding
              w: MATH.max(
                (widthAuto ? contentSize.w : scrollSize.w) + paddingAbsoluteX,
                hostAbsoluteRectSize.w
              ),
              h: MATH.max(
                (heightAuto ? contentSize.h : scrollSize.h) + paddingAbsoluteY,
                hostAbsoluteRectSize.h
              ),
            };
            contentGlueSize.c = checkCacheAutoForce(
              contentGlueSize,
              _contentGlueSizeCache
            );
            _contentGlueSizeCache = contentGlueSize;

            //apply correct contentGlue size
            if (sizeAutoCapable) {
              //size contentGlue correctly to make sure the element has correct size if the sizing switches to auto
              if (contentGlueSize.c || heightAuto || widthAuto) {
                contentGlueElementCSS[_strWidth] = contentGlueSize.w;
                contentGlueElementCSS[_strHeight] = contentGlueSize.h;

                //textarea-sizes are already calculated correctly at this point
                if (!_isTextarea) {
                  contentSize = {
                    //use clientSize because natively overlaidScrollbars add borders
                    w: contentMeasureElement[LEXICON.cW],
                    h: contentMeasureElement[LEXICON.cH],
                  };
                }
              }
              var textareaCoverCSS = {};
              var setContentGlueElementCSSfunction = function (horizontal) {
                var scrollbarVars = getScrollbarVars(horizontal);
                var wh = scrollbarVars._w_h;
                var strWH = scrollbarVars._width_height;
                var autoSize = horizontal ? widthAuto : heightAuto;
                var borderSize = horizontal ? _borderX : _borderY;
                var paddingSize = horizontal ? _paddingX : _paddingY;
                var marginSize = horizontal ? _marginX : _marginY;
                var viewportSize =
                  _viewportSize[wh] -
                  borderSize -
                  marginSize -
                  (_isBorderBox ? 0 : paddingSize);

                //make contentGlue size -1 if element is not auto sized, to make sure that a resize event happens when the element shrinks
                if (!autoSize || (!autoSize && border.c))
                  contentGlueElementCSS[strWH] = hostAbsoluteRectSize[wh] - 1;

                //if size is auto and host is smaller than size as min size, make content glue size -1 to make sure size changes will be detected (this is only needed if padding is 0)
                if (
                  autoSize &&
                  contentSize[wh] < viewportSize &&
                  (horizontal && _isTextarea ? !textareaAutoWrapping : true)
                ) {
                  if (_isTextarea)
                    textareaCoverCSS[strWH] =
                      parseToZeroOrNumber(_textareaCoverElement.css(strWH)) - 1;
                  contentGlueElementCSS[strWH] -= 1;
                }

                //make sure content glue size is at least 1
                if (contentSize[wh] > 0)
                  contentGlueElementCSS[strWH] = MATH.max(
                    1,
                    contentGlueElementCSS[strWH]
                  );
              };
              setContentGlueElementCSSfunction(true);
              setContentGlueElementCSSfunction(false);

              if (_isTextarea) _textareaCoverElement.css(textareaCoverCSS);
              _contentGlueElement.css(contentGlueElementCSS);
            }
            if (widthAuto) contentElementCSS[_strWidth] = _strHundredPercent;
            if (widthAuto && !_isBorderBox && !_mutationObserversConnected)
              contentElementCSS[_strFloat] = "none";

            //apply and reset content style
            _contentElement.css(contentElementCSS);
            contentElementCSS = {};

            //measure again, but this time all correct sizes:
            var contentScrollSize = {
              w: contentMeasureElement[LEXICON.sW],
              h: contentMeasureElement[LEXICON.sH],
            };
            contentScrollSize.c = contentSizeChanged = checkCacheAutoForce(
              contentScrollSize,
              _contentScrollSizeCache
            );
            _contentScrollSizeCache = contentScrollSize;

            //refresh viewport size after correct measuring
            _viewportSize = getViewportSize();

            hostSize = getHostSize();
            hostSizeChanged = checkCacheAutoForce(hostSize, _hostSizeCache);
            _hostSizeCache = hostSize;

            var hideOverflowForceTextarea =
              _isTextarea && (_viewportSize.w === 0 || _viewportSize.h === 0);
            var previousOverflowAmount = _overflowAmountCache;
            var overflowBehaviorIsVS = {};
            var overflowBehaviorIsVH = {};
            var overflowBehaviorIsS = {};
            var overflowAmount = {};
            var hasOverflow = {};
            var hideOverflow = {};
            var canScroll = {};
            var viewportRect = _paddingElementNative[LEXICON.bCR]();
            var setOverflowVariables = function (horizontal) {
              var scrollbarVars = getScrollbarVars(horizontal);
              var scrollbarVarsInverted = getScrollbarVars(!horizontal);
              var xyI = scrollbarVarsInverted._x_y;
              var xy = scrollbarVars._x_y;
              var wh = scrollbarVars._w_h;
              var widthHeight = scrollbarVars._width_height;
              var scrollMax = _strScroll + scrollbarVars._Left_Top + "Max";
              var fractionalOverflowAmount = viewportRect[widthHeight]
                ? MATH.abs(viewportRect[widthHeight] - _viewportSize[wh])
                : 0;
              var checkFractionalOverflowAmount =
                previousOverflowAmount &&
                previousOverflowAmount[xy] > 0 &&
                _viewportElementNative[scrollMax] === 0;
              overflowBehaviorIsVS[xy] = overflowBehavior[xy] === "v-s";
              overflowBehaviorIsVH[xy] = overflowBehavior[xy] === "v-h";
              overflowBehaviorIsS[xy] = overflowBehavior[xy] === "s";
              overflowAmount[xy] = MATH.max(
                0,
                MATH.round((contentScrollSize[wh] - _viewportSize[wh]) * 100) /
                  100
              );
              overflowAmount[xy] *=
                hideOverflowForceTextarea ||
                (checkFractionalOverflowAmount &&
                  fractionalOverflowAmount > 0 &&
                  fractionalOverflowAmount < 1)
                  ? 0
                  : 1;
              hasOverflow[xy] = overflowAmount[xy] > 0;

              //hideOverflow:
              //x || y : true === overflow is hidden by "overflow: scroll" OR "overflow: hidden"
              //xs || ys : true === overflow is hidden by "overflow: scroll"
              hideOverflow[xy] =
                overflowBehaviorIsVS[xy] || overflowBehaviorIsVH[xy]
                  ? hasOverflow[xyI] &&
                    !overflowBehaviorIsVS[xyI] &&
                    !overflowBehaviorIsVH[xyI]
                  : hasOverflow[xy];
              hideOverflow[xy + "s"] = hideOverflow[xy]
                ? overflowBehaviorIsS[xy] || overflowBehaviorIsVS[xy]
                : false;

              canScroll[xy] = hasOverflow[xy] && hideOverflow[xy + "s"];
            };
            setOverflowVariables(true);
            setOverflowVariables(false);

            overflowAmount.c = checkCacheAutoForce(
              overflowAmount,
              _overflowAmountCache
            );
            _overflowAmountCache = overflowAmount;
            hasOverflow.c = checkCacheAutoForce(hasOverflow, _hasOverflowCache);
            _hasOverflowCache = hasOverflow;
            hideOverflow.c = checkCacheAutoForce(
              hideOverflow,
              _hideOverflowCache
            );
            _hideOverflowCache = hideOverflow;

            //if native scrollbar is overlay at x OR y axis, prepare DOM
            if (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y) {
              var borderDesign = "px solid transparent";
              var contentArrangeElementCSS = {};
              var arrangeContent = {};
              var arrangeChanged = force;
              var setContentElementCSS;

              if (hasOverflow.x || hasOverflow.y) {
                arrangeContent.w =
                  _nativeScrollbarIsOverlaid.y && hasOverflow.y
                    ? contentScrollSize.w + _overlayScrollbarDummySize.y
                    : _strEmpty;
                arrangeContent.h =
                  _nativeScrollbarIsOverlaid.x && hasOverflow.x
                    ? contentScrollSize.h + _overlayScrollbarDummySize.x
                    : _strEmpty;
                arrangeChanged = checkCacheAutoForce(
                  arrangeContent,
                  _arrangeContentSizeCache
                );
                _arrangeContentSizeCache = arrangeContent;
              }

              if (
                hasOverflow.c ||
                hideOverflow.c ||
                contentScrollSize.c ||
                cssDirectionChanged ||
                widthAutoChanged ||
                heightAutoChanged ||
                widthAuto ||
                heightAuto ||
                ignoreOverlayScrollbarHidingChanged
              ) {
                contentElementCSS[_strMarginMinus + isRTLRight] =
                  contentElementCSS[_strBorderMinus + isRTLRight] = _strEmpty;
                setContentElementCSS = function (horizontal) {
                  var scrollbarVars = getScrollbarVars(horizontal);
                  var scrollbarVarsInverted = getScrollbarVars(!horizontal);
                  var xy = scrollbarVars._x_y;
                  var strDirection = horizontal ? _strBottom : isRTLLeft;
                  var invertedAutoSize = horizontal ? heightAuto : widthAuto;

                  if (
                    _nativeScrollbarIsOverlaid[xy] &&
                    hasOverflow[xy] &&
                    hideOverflow[xy + "s"]
                  ) {
                    contentElementCSS[_strMarginMinus + strDirection] =
                      invertedAutoSize
                        ? ignoreOverlayScrollbarHiding
                          ? _strEmpty
                          : _overlayScrollbarDummySize[xy]
                        : _strEmpty;
                    contentElementCSS[_strBorderMinus + strDirection] =
                      (horizontal ? !invertedAutoSize : true) &&
                      !ignoreOverlayScrollbarHiding
                        ? _overlayScrollbarDummySize[xy] + borderDesign
                        : _strEmpty;
                  } else {
                    arrangeContent[scrollbarVarsInverted._w_h] =
                      contentElementCSS[_strMarginMinus + strDirection] =
                      contentElementCSS[_strBorderMinus + strDirection] =
                        _strEmpty;
                    arrangeChanged = true;
                  }
                };

                if (_nativeScrollbarStyling) {
                  addRemoveClass(
                    _viewportElement,
                    _classNameViewportNativeScrollbarsInvisible,
                    !ignoreOverlayScrollbarHiding
                  );
                } else {
                  setContentElementCSS(true);
                  setContentElementCSS(false);
                }
              }
              if (ignoreOverlayScrollbarHiding) {
                arrangeContent.w = arrangeContent.h = _strEmpty;
                arrangeChanged = true;
              }
              if (arrangeChanged && !_nativeScrollbarStyling) {
                contentArrangeElementCSS[_strWidth] = hideOverflow.y
                  ? arrangeContent.w
                  : _strEmpty;
                contentArrangeElementCSS[_strHeight] = hideOverflow.x
                  ? arrangeContent.h
                  : _strEmpty;

                if (!_contentArrangeElement) {
                  _contentArrangeElement = FRAMEWORK(
                    generateDiv(_classNameContentArrangeElement)
                  );
                  _viewportElement.prepend(_contentArrangeElement);
                }
                _contentArrangeElement.css(contentArrangeElementCSS);
              }
              _contentElement.css(contentElementCSS);
            }

            var viewportElementCSS = {};
            var paddingElementCSS = {};
            var setViewportCSS;
            if (
              hostSizeChanged ||
              hasOverflow.c ||
              hideOverflow.c ||
              contentScrollSize.c ||
              overflowBehaviorChanged ||
              boxSizingChanged ||
              ignoreOverlayScrollbarHidingChanged ||
              cssDirectionChanged ||
              clipAlwaysChanged ||
              heightAutoChanged
            ) {
              viewportElementCSS[isRTLRight] = _strEmpty;
              setViewportCSS = function (horizontal) {
                var scrollbarVars = getScrollbarVars(horizontal);
                var scrollbarVarsInverted = getScrollbarVars(!horizontal);
                var xy = scrollbarVars._x_y;
                var XY = scrollbarVars._X_Y;
                var strDirection = horizontal ? _strBottom : isRTLLeft;

                var reset = function () {
                  viewportElementCSS[strDirection] = _strEmpty;
                  _contentBorderSize[scrollbarVarsInverted._w_h] = 0;
                };
                if (hasOverflow[xy] && hideOverflow[xy + "s"]) {
                  viewportElementCSS[strOverflow + XY] = _strScroll;
                  if (ignoreOverlayScrollbarHiding || _nativeScrollbarStyling) {
                    reset();
                  } else {
                    viewportElementCSS[strDirection] =
                      -(_nativeScrollbarIsOverlaid[xy]
                        ? _overlayScrollbarDummySize[xy]
                        : _nativeScrollbarSize[xy]);
                    _contentBorderSize[scrollbarVarsInverted._w_h] =
                      _nativeScrollbarIsOverlaid[xy]
                        ? _overlayScrollbarDummySize[scrollbarVarsInverted._x_y]
                        : 0;
                  }
                } else {
                  viewportElementCSS[strOverflow + XY] = _strEmpty;
                  reset();
                }
              };
              setViewportCSS(true);
              setViewportCSS(false);

              // if the scroll container is too small and if there is any overflow with no overlay scrollbar (and scrollbar styling isn't possible),
              // make viewport element greater in size (Firefox hide Scrollbars fix)
              // because firefox starts hiding scrollbars on too small elements
              // with this behavior the overflow calculation may be incorrect or the scrollbars would appear suddenly
              // https://bugzilla.mozilla.org/show_bug.cgi?id=292284
              if (
                !_nativeScrollbarStyling &&
                (_viewportSize.h < _nativeScrollbarMinSize.x ||
                  _viewportSize.w < _nativeScrollbarMinSize.y) &&
                ((hasOverflow.x &&
                  hideOverflow.x &&
                  !_nativeScrollbarIsOverlaid.x) ||
                  (hasOverflow.y &&
                    hideOverflow.y &&
                    !_nativeScrollbarIsOverlaid.y))
              ) {
                viewportElementCSS[_strPaddingMinus + _strTop] =
                  _nativeScrollbarMinSize.x;
                viewportElementCSS[_strMarginMinus + _strTop] =
                  -_nativeScrollbarMinSize.x;

                viewportElementCSS[_strPaddingMinus + isRTLRight] =
                  _nativeScrollbarMinSize.y;
                viewportElementCSS[_strMarginMinus + isRTLRight] =
                  -_nativeScrollbarMinSize.y;
              } else {
                viewportElementCSS[_strPaddingMinus + _strTop] =
                  viewportElementCSS[_strMarginMinus + _strTop] =
                  viewportElementCSS[_strPaddingMinus + isRTLRight] =
                  viewportElementCSS[_strMarginMinus + isRTLRight] =
                    _strEmpty;
              }
              viewportElementCSS[_strPaddingMinus + isRTLLeft] =
                viewportElementCSS[_strMarginMinus + isRTLLeft] = _strEmpty;

              //if there is any overflow (x OR y axis) and this overflow shall be hidden, make overflow hidden, else overflow visible
              if (
                (hasOverflow.x && hideOverflow.x) ||
                (hasOverflow.y && hideOverflow.y) ||
                hideOverflowForceTextarea
              ) {
                //only hide if is Textarea
                if (_isTextarea && hideOverflowForceTextarea) {
                  paddingElementCSS[strOverflowX] = paddingElementCSS[
                    strOverflowY
                  ] = strHidden;
                }
              } else {
                if (
                  !clipAlways ||
                  overflowBehaviorIsVH.x ||
                  overflowBehaviorIsVS.x ||
                  overflowBehaviorIsVH.y ||
                  overflowBehaviorIsVS.y
                ) {
                  //only un-hide if Textarea
                  if (_isTextarea) {
                    paddingElementCSS[strOverflowX] = paddingElementCSS[
                      strOverflowY
                    ] = _strEmpty;
                  }
                  viewportElementCSS[strOverflowX] = viewportElementCSS[
                    strOverflowY
                  ] = strVisible;
                }
              }

              _paddingElement.css(paddingElementCSS);
              _viewportElement.css(viewportElementCSS);
              viewportElementCSS = {};

              //force soft redraw in webkit because without the scrollbars will may appear because DOM wont be redrawn under special conditions
              if (
                (hasOverflow.c ||
                  boxSizingChanged ||
                  widthAutoChanged ||
                  heightAutoChanged) &&
                !(_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)
              ) {
                var elementStyle = _contentElementNative[LEXICON.s];
                var dump;
                elementStyle.webkitTransform = "scale(1)";
                elementStyle.display = "run-in";
                dump = _contentElementNative[LEXICON.oH];
                elementStyle.display = _strEmpty; //|| dump; //use dump to prevent it from deletion if minify
                elementStyle.webkitTransform = _strEmpty;
              }
              /*
                            //force hard redraw in webkit if native overlaid scrollbars shall appear
                            if (ignoreOverlayScrollbarHidingChanged && ignoreOverlayScrollbarHiding) {
                                _hostElement.hide();
                                var dump = _hostElementNative[LEXICON.oH];
                                _hostElement.show();
                            }
                            */
            }

            //change to direction RTL and width auto Bugfix in Webkit
            //without this fix, the DOM still thinks the scrollbar is LTR and thus the content is shifted to the left
            contentElementCSS = {};
            if (cssDirectionChanged || widthAutoChanged || heightAutoChanged) {
              if (_isRTL && widthAuto) {
                var floatTmp = _contentElement.css(_strFloat);
                var posLeftWithoutFloat = MATH.round(
                  _contentElement
                    .css(_strFloat, _strEmpty)
                    .css(_strLeft, _strEmpty)
                    .position().left
                );
                _contentElement.css(_strFloat, floatTmp);
                var posLeftWithFloat = MATH.round(
                  _contentElement.position().left
                );

                if (posLeftWithoutFloat !== posLeftWithFloat)
                  contentElementCSS[_strLeft] = posLeftWithoutFloat;
              } else {
                contentElementCSS[_strLeft] = _strEmpty;
              }
            }
            _contentElement.css(contentElementCSS);

            //handle scroll position
            if (_isTextarea && contentSizeChanged) {
              var textareaInfo = getTextareaInfo();
              if (textareaInfo) {
                var textareaRowsChanged =
                  _textareaInfoCache === undefined
                    ? true
                    : textareaInfo._rows !== _textareaInfoCache._rows;
                var cursorRow = textareaInfo._cursorRow;
                var cursorCol = textareaInfo._cursorColumn;
                var widestRow = textareaInfo._widestRow;
                var lastRow = textareaInfo._rows;
                var lastCol = textareaInfo._columns;
                var cursorPos = textareaInfo._cursorPosition;
                var cursorMax = textareaInfo._cursorMax;
                var cursorIsLastPosition =
                  cursorPos >= cursorMax && _textareaHasFocus;
                var textareaScrollAmount = {
                  x:
                    !textareaAutoWrapping &&
                    cursorCol === lastCol &&
                    cursorRow === widestRow
                      ? _overflowAmountCache.x
                      : -1,
                  y: (
                    textareaAutoWrapping
                      ? cursorIsLastPosition ||
                        (textareaRowsChanged &&
                          (previousOverflowAmount
                            ? currScroll.y === previousOverflowAmount.y
                            : false))
                      : (cursorIsLastPosition || textareaRowsChanged) &&
                        cursorRow === lastRow
                  )
                    ? _overflowAmountCache.y
                    : -1,
                };
                currScroll.x =
                  textareaScrollAmount.x > -1
                    ? _isRTL && _normalizeRTLCache && _rtlScrollBehavior.i
                      ? 0
                      : textareaScrollAmount.x
                    : currScroll.x; //if inverted, scroll to 0 -> normalized this means to max scroll offset.
                currScroll.y =
                  textareaScrollAmount.y > -1
                    ? textareaScrollAmount.y
                    : currScroll.y;
              }
              _textareaInfoCache = textareaInfo;
            }
            if (
              _isRTL &&
              _rtlScrollBehavior.i &&
              _nativeScrollbarIsOverlaid.y &&
              hasOverflow.x &&
              _normalizeRTLCache
            )
              currScroll.x += _contentBorderSize.w || 0;
            if (widthAuto) _hostElement[_strScrollLeft](0);
            if (heightAuto) _hostElement[_strScrollTop](0);
            _viewportElement[_strScrollLeft](currScroll.x)[_strScrollTop](
              currScroll.y
            );

            //scrollbars management:
            var scrollbarsVisibilityVisible = scrollbarsVisibility === "v";
            var scrollbarsVisibilityHidden = scrollbarsVisibility === "h";
            var scrollbarsVisibilityAuto = scrollbarsVisibility === "a";
            var refreshScrollbarsVisibility = function (showX, showY) {
              showY = showY === undefined ? showX : showY;
              refreshScrollbarAppearance(true, showX, canScroll.x);
              refreshScrollbarAppearance(false, showY, canScroll.y);
            };

            //manage class name which indicates scrollable overflow
            addRemoveClass(
              _hostElement,
              _classNameHostOverflow,
              hideOverflow.x || hideOverflow.y
            );
            addRemoveClass(
              _hostElement,
              _classNameHostOverflowX,
              hideOverflow.x
            );
            addRemoveClass(
              _hostElement,
              _classNameHostOverflowY,
              hideOverflow.y
            );

            //add or remove rtl class name for styling purposes except when its body, then the scrollbar stays
            if (cssDirectionChanged && !_isBody) {
              addRemoveClass(_hostElement, _classNameHostRTL, _isRTL);
            }

            //manage the resize feature (CSS3 resize "polyfill" for this plugin)
            if (_isBody) addClass(_hostElement, _classNameHostResizeDisabled);
            if (resizeChanged) {
              addRemoveClass(
                _hostElement,
                _classNameHostResizeDisabled,
                _resizeNone
              );
              addRemoveClass(
                _scrollbarCornerElement,
                _classNameScrollbarCornerResize,
                !_resizeNone
              );
              addRemoveClass(
                _scrollbarCornerElement,
                _classNameScrollbarCornerResizeB,
                _resizeBoth
              );
              addRemoveClass(
                _scrollbarCornerElement,
                _classNameScrollbarCornerResizeH,
                _resizeHorizontal
              );
              addRemoveClass(
                _scrollbarCornerElement,
                _classNameScrollbarCornerResizeV,
                _resizeVertical
              );
            }

            //manage the scrollbars general visibility + the scrollbar interactivity (unusable class name)
            if (
              scrollbarsVisibilityChanged ||
              overflowBehaviorChanged ||
              hideOverflow.c ||
              hasOverflow.c ||
              ignoreOverlayScrollbarHidingChanged
            ) {
              if (ignoreOverlayScrollbarHiding) {
                if (ignoreOverlayScrollbarHidingChanged) {
                  removeClass(_hostElement, _classNameHostScrolling);
                  if (ignoreOverlayScrollbarHiding) {
                    refreshScrollbarsVisibility(false);
                  }
                }
              } else if (scrollbarsVisibilityAuto) {
                refreshScrollbarsVisibility(canScroll.x, canScroll.y);
              } else if (scrollbarsVisibilityVisible) {
                refreshScrollbarsVisibility(true);
              } else if (scrollbarsVisibilityHidden) {
                refreshScrollbarsVisibility(false);
              }
            }

            //manage the scrollbars auto hide feature (auto hide them after specific actions)
            if (
              scrollbarsAutoHideChanged ||
              ignoreOverlayScrollbarHidingChanged
            ) {
              setupHostMouseTouchEvents(
                !_scrollbarsAutoHideLeave && !_scrollbarsAutoHideMove
              );
              refreshScrollbarsAutoHide(
                _scrollbarsAutoHideNever,
                !_scrollbarsAutoHideNever
              );
            }

            //manage scrollbars handle length & offset - don't remove!
            if (
              hostSizeChanged ||
              overflowAmount.c ||
              heightAutoChanged ||
              widthAutoChanged ||
              resizeChanged ||
              boxSizingChanged ||
              paddingAbsoluteChanged ||
              ignoreOverlayScrollbarHidingChanged ||
              cssDirectionChanged
            ) {
              refreshScrollbarHandleLength(true);
              refreshScrollbarHandleOffset(true);
              refreshScrollbarHandleLength(false);
              refreshScrollbarHandleOffset(false);
            }

            //manage interactivity
            if (scrollbarsClickScrollingChanged)
              refreshScrollbarsInteractive(true, scrollbarsClickScrolling);
            if (scrollbarsDragScrollingChanged)
              refreshScrollbarsInteractive(false, scrollbarsDragScrolling);

            //callbacks:
            dispatchCallback(
              "onDirectionChanged",
              {
                isRTL: _isRTL,
                dir: cssDirection,
              },
              cssDirectionChanged
            );
            dispatchCallback(
              "onHostSizeChanged",
              {
                width: _hostSizeCache.w,
                height: _hostSizeCache.h,
              },
              hostSizeChanged
            );
            dispatchCallback(
              "onContentSizeChanged",
              {
                width: _contentScrollSizeCache.w,
                height: _contentScrollSizeCache.h,
              },
              contentSizeChanged
            );
            dispatchCallback(
              "onOverflowChanged",
              {
                x: hasOverflow.x,
                y: hasOverflow.y,
                xScrollable: hideOverflow.xs,
                yScrollable: hideOverflow.ys,
                clipped: hideOverflow.x || hideOverflow.y,
              },
              hasOverflow.c || hideOverflow.c
            );
            dispatchCallback(
              "onOverflowAmountChanged",
              {
                x: overflowAmount.x,
                y: overflowAmount.y,
              },
              overflowAmount.c
            );
          }

          //fix body min size
          if (
            _isBody &&
            _bodyMinSizeCache &&
            (_hasOverflowCache.c || _bodyMinSizeCache.c)
          ) {
            //its possible that no min size was measured until now, because the content arrange element was just added now, in this case, measure now the min size.
            if (!_bodyMinSizeCache.f) bodyMinSizeChanged();
            if (_nativeScrollbarIsOverlaid.y && _hasOverflowCache.x)
              _contentElement.css(
                _strMinMinus + _strWidth,
                _bodyMinSizeCache.w + _overlayScrollbarDummySize.y
              );
            if (_nativeScrollbarIsOverlaid.x && _hasOverflowCache.y)
              _contentElement.css(
                _strMinMinus + _strHeight,
                _bodyMinSizeCache.h + _overlayScrollbarDummySize.x
              );
            _bodyMinSizeCache.c = false;
          }

          if (_initialized && changedOptions.updateOnLoad) {
            updateElementsOnLoad();
          }

          //freezeResizeObserver(_sizeObserverElement, false);
          //freezeResizeObserver(_sizeAutoObserverElement, false);

          dispatchCallback("onUpdated", { forced: force });
        }

        /**
         * Updates the found elements of which the load event shall be handled.
         */
        function updateElementsOnLoad() {
          if (!_isTextarea) {
            eachUpdateOnLoad(function (i, updateOnLoadSelector) {
              _contentElement.find(updateOnLoadSelector).each(function (i, el) {
                // if element doesn't have a updateOnLoadCallback applied
                if (COMPATIBILITY.inA(el, _updateOnLoadElms) < 0) {
                  _updateOnLoadElms.push(el);
                  FRAMEWORK(el)
                    .off(_updateOnLoadEventName, updateOnLoadCallback)
                    .on(_updateOnLoadEventName, updateOnLoadCallback);
                }
              });
            });
          }
        }

        //==== Options ====//

        /**
         * Sets new options but doesn't call the update method.
         * @param newOptions The object which contains the new options.
         * @returns {*} A object which contains the changed options.
         */
        function setOptions(newOptions) {
          var validatedOpts = _pluginsOptions._validate(
            newOptions,
            _pluginsOptions._template,
            true,
            _currentOptions
          );

          _currentOptions = extendDeep(
            {},
            _currentOptions,
            validatedOpts._default
          );
          _currentPreparedOptions = extendDeep(
            {},
            _currentPreparedOptions,
            validatedOpts._prepared
          );

          return validatedOpts._prepared;
        }

        //==== Structure ====//

        /**
         * Builds or destroys the wrapper and helper DOM elements.
         * @param destroy Indicates whether the DOM shall be build or destroyed.
         */
        /**
         * Builds or destroys the wrapper and helper DOM elements.
         * @param destroy Indicates whether the DOM shall be build or destroyed.
         */
        function setupStructureDOM(destroy) {
          var strParent = "parent";
          var classNameResizeObserverHost = "os-resize-observer-host";
          var classNameTextareaElementFull =
            _classNameTextareaElement + _strSpace + _classNameTextInherit;
          var textareaClass = _isTextarea
            ? _strSpace + _classNameTextInherit
            : _strEmpty;
          var adoptAttrs = _currentPreparedOptions.textarea.inheritedAttrs;
          var adoptAttrsMap = {};
          var applyAdoptedAttrs = function () {
            var applyAdoptedAttrsElm = destroy ? _targetElement : _hostElement;
            each(adoptAttrsMap, function (key, value) {
              if (type(value) == TYPES.s) {
                if (key == LEXICON.c) applyAdoptedAttrsElm.addClass(value);
                else applyAdoptedAttrsElm.attr(key, value);
              }
            });
          };
          var hostElementClassNames = [
            _classNameHostElement,
            _classNameHostElementForeign,
            _classNameHostTextareaElement,
            _classNameHostResizeDisabled,
            _classNameHostRTL,
            _classNameHostScrollbarHorizontalHidden,
            _classNameHostScrollbarVerticalHidden,
            _classNameHostTransition,
            _classNameHostScrolling,
            _classNameHostOverflow,
            _classNameHostOverflowX,
            _classNameHostOverflowY,
            _classNameThemeNone,
            _classNameTextareaElement,
            _classNameTextInherit,
            _classNameCache,
          ].join(_strSpace);
          var hostElementCSS = {};

          //get host element as first element, because that's the most upper element and required for the other elements
          _hostElement =
            _hostElement ||
            (_isTextarea
              ? _domExists
                ? _targetElement[strParent]()
                    [strParent]()
                    [strParent]()
                    [strParent]()
                : FRAMEWORK(generateDiv(_classNameHostTextareaElement))
              : _targetElement);
          _contentElement =
            _contentElement ||
            selectOrGenerateDivByClass(
              _classNameContentElement + textareaClass
            );
          _viewportElement =
            _viewportElement ||
            selectOrGenerateDivByClass(
              _classNameViewportElement + textareaClass
            );
          _paddingElement =
            _paddingElement ||
            selectOrGenerateDivByClass(
              _classNamePaddingElement + textareaClass
            );
          _sizeObserverElement =
            _sizeObserverElement ||
            selectOrGenerateDivByClass(classNameResizeObserverHost);
          _textareaCoverElement =
            _textareaCoverElement ||
            (_isTextarea
              ? selectOrGenerateDivByClass(_classNameTextareaCoverElement)
              : undefined);

          //add this class to workaround class changing issues with UI frameworks especially Vue
          if (_domExists) addClass(_hostElement, _classNameHostElementForeign);

          //on destroy, remove all generated class names from the host element before collecting the adopted attributes
          //to prevent adopting generated class names
          if (destroy) removeClass(_hostElement, hostElementClassNames);

          //collect all adopted attributes
          adoptAttrs =
            type(adoptAttrs) == TYPES.s
              ? adoptAttrs.split(_strSpace)
              : adoptAttrs;
          if (COMPATIBILITY.isA(adoptAttrs) && _isTextarea) {
            each(adoptAttrs, function (i, v) {
              if (type(v) == TYPES.s) {
                adoptAttrsMap[v] = destroy
                  ? _hostElement.attr(v)
                  : _targetElement.attr(v);
              }
            });
          }

          if (!destroy) {
            if (_isTextarea) {
              if (!_currentPreparedOptions.sizeAutoCapable) {
                hostElementCSS[_strWidth] = _targetElement.css(_strWidth);
                hostElementCSS[_strHeight] = _targetElement.css(_strHeight);
              }

              if (!_domExists)
                _targetElement
                  .addClass(_classNameTextInherit)
                  .wrap(_hostElement);

              //jQuery clones elements in wrap functions, so we have to select them again
              _hostElement = _targetElement[strParent]().css(hostElementCSS);
            }

            if (!_domExists) {
              //add the correct class to the target element
              addClass(
                _targetElement,
                _isTextarea
                  ? classNameTextareaElementFull
                  : _classNameHostElement
              );

              //wrap the content into the generated elements to create the required DOM
              _hostElement
                .wrapInner(_contentElement)
                .wrapInner(_viewportElement)
                .wrapInner(_paddingElement)
                .prepend(_sizeObserverElement);

              //jQuery clones elements in wrap functions, so we have to select them again
              _contentElement = findFirst(
                _hostElement,
                _strDot + _classNameContentElement
              );
              _viewportElement = findFirst(
                _hostElement,
                _strDot + _classNameViewportElement
              );
              _paddingElement = findFirst(
                _hostElement,
                _strDot + _classNamePaddingElement
              );

              if (_isTextarea) {
                _contentElement.prepend(_textareaCoverElement);
                applyAdoptedAttrs();
              }
            }

            if (_nativeScrollbarStyling)
              addClass(
                _viewportElement,
                _classNameViewportNativeScrollbarsInvisible
              );
            if (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)
              addClass(
                _viewportElement,
                _classNameViewportNativeScrollbarsOverlaid
              );
            if (_isBody) addClass(_htmlElement, _classNameHTMLElement);

            _sizeObserverElementNative = _sizeObserverElement[0];
            _hostElementNative = _hostElement[0];
            _paddingElementNative = _paddingElement[0];
            _viewportElementNative = _viewportElement[0];
            _contentElementNative = _contentElement[0];

            updateViewportAttrsFromTarget();
          } else {
            if (_domExists && _initialized) {
              //clear size observer
              _sizeObserverElement.children().remove();

              //remove the style property and classes from already generated elements
              each(
                [
                  _paddingElement,
                  _viewportElement,
                  _contentElement,
                  _textareaCoverElement,
                ],
                function (i, elm) {
                  if (elm) {
                    removeClass(
                      elm.removeAttr(LEXICON.s),
                      _classNamesDynamicDestroy
                    );
                  }
                }
              );

              //add classes to the host element which was removed previously to match the expected DOM
              addClass(
                _hostElement,
                _isTextarea
                  ? _classNameHostTextareaElement
                  : _classNameHostElement
              );
            } else {
              //remove size observer
              remove(_sizeObserverElement);

              //unwrap the content to restore DOM
              _contentElement.contents().unwrap().unwrap().unwrap();

              if (_isTextarea) {
                _targetElement.unwrap();
                remove(_hostElement);
                remove(_textareaCoverElement);
                applyAdoptedAttrs();
              }
            }

            if (_isTextarea) _targetElement.removeAttr(LEXICON.s);

            if (_isBody) removeClass(_htmlElement, _classNameHTMLElement);
          }
        }

        /**
         * Adds or removes all wrapper elements interactivity events.
         * @param destroy Indicates whether the Events shall be added or removed.
         */
        function setupStructureEvents() {
          var textareaKeyDownRestrictedKeyCodes = [
            112,
            113,
            114,
            115,
            116,
            117,
            118,
            119,
            120,
            121,
            123, //F1 to F12
            33,
            34, //page up, page down
            37,
            38,
            39,
            40, //left, up, right, down arrows
            16,
            17,
            18,
            19,
            20,
            144, //Shift, Ctrl, Alt, Pause, CapsLock, NumLock
          ];
          var textareaKeyDownKeyCodesList = [];
          var textareaUpdateIntervalID;
          var scrollStopTimeoutId;
          var scrollStopDelay = 175;
          var strFocus = "focus";

          function updateTextarea(doClearInterval) {
            textareaUpdate();
            _base.update(_strAuto);
            if (doClearInterval && _autoUpdateRecommended)
              clearInterval(textareaUpdateIntervalID);
          }
          function textareaOnScroll(event) {
            _targetElement[_strScrollLeft](
              _rtlScrollBehavior.i && _normalizeRTLCache ? 9999999 : 0
            );
            _targetElement[_strScrollTop](0);
            COMPATIBILITY.prvD(event);
            COMPATIBILITY.stpP(event);
            return false;
          }
          function textareaOnDrop(event) {
            setTimeout(function () {
              if (!_destroyed) updateTextarea();
            }, 50);
          }
          function textareaOnFocus() {
            _textareaHasFocus = true;
            addClass(_hostElement, strFocus);
          }
          function textareaOnFocusout() {
            _textareaHasFocus = false;
            textareaKeyDownKeyCodesList = [];
            removeClass(_hostElement, strFocus);
            updateTextarea(true);
          }
          function textareaOnKeyDown(event) {
            var keyCode = event.keyCode;

            if (inArray(keyCode, textareaKeyDownRestrictedKeyCodes) < 0) {
              if (!textareaKeyDownKeyCodesList[LEXICON.l]) {
                updateTextarea();
                textareaUpdateIntervalID = setInterval(
                  updateTextarea,
                  1000 / 60
                );
              }
              if (inArray(keyCode, textareaKeyDownKeyCodesList) < 0)
                textareaKeyDownKeyCodesList.push(keyCode);
            }
          }
          function textareaOnKeyUp(event) {
            var keyCode = event.keyCode;
            var index = inArray(keyCode, textareaKeyDownKeyCodesList);

            if (inArray(keyCode, textareaKeyDownRestrictedKeyCodes) < 0) {
              if (index > -1) textareaKeyDownKeyCodesList.splice(index, 1);
              if (!textareaKeyDownKeyCodesList[LEXICON.l]) updateTextarea(true);
            }
          }
          function contentOnTransitionEnd(event) {
            if (_autoUpdateCache === true) return;
            event = event.originalEvent || event;
            if (isSizeAffectingCSSProperty(event.propertyName))
              _base.update(_strAuto);
          }
          function viewportOnScroll(event) {
            if (!_sleeping) {
              if (scrollStopTimeoutId !== undefined)
                clearTimeout(scrollStopTimeoutId);
              else {
                if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)
                  refreshScrollbarsAutoHide(true);

                if (!nativeOverlayScrollbarsAreActive())
                  addClass(_hostElement, _classNameHostScrolling);

                dispatchCallback("onScrollStart", event);
              }

              //if a scrollbars handle gets dragged, the mousemove event is responsible for refreshing the handle offset
              //because if CSS scroll-snap is used, the handle offset gets only refreshed on every snap point
              //this looks laggy & clunky, it looks much better if the offset refreshes with the mousemove
              if (!_scrollbarsHandlesDefineScrollPos) {
                refreshScrollbarHandleOffset(true);
                refreshScrollbarHandleOffset(false);
              }
              dispatchCallback("onScroll", event);

              scrollStopTimeoutId = setTimeout(function () {
                if (!_destroyed) {
                  //OnScrollStop:
                  clearTimeout(scrollStopTimeoutId);
                  scrollStopTimeoutId = undefined;

                  if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)
                    refreshScrollbarsAutoHide(false);

                  if (!nativeOverlayScrollbarsAreActive())
                    removeClass(_hostElement, _classNameHostScrolling);

                  dispatchCallback("onScrollStop", event);
                }
              }, scrollStopDelay);
            }
          }

          if (_isTextarea) {
            if (_msieVersion > 9 || !_autoUpdateRecommended) {
              addDestroyEventListener(_targetElement, "input", updateTextarea);
            } else {
              addDestroyEventListener(
                _targetElement,
                [_strKeyDownEvent, _strKeyUpEvent],
                [textareaOnKeyDown, textareaOnKeyUp]
              );
            }

            addDestroyEventListener(
              _targetElement,
              [_strScroll, "drop", strFocus, strFocus + "out"],
              [
                textareaOnScroll,
                textareaOnDrop,
                textareaOnFocus,
                textareaOnFocusout,
              ]
            );
          } else {
            addDestroyEventListener(
              _contentElement,
              _strTransitionEndEvent,
              contentOnTransitionEnd
            );
          }
          addDestroyEventListener(
            _viewportElement,
            _strScroll,
            viewportOnScroll,
            true
          );
        }

        //==== Scrollbars ====//

        /**
         * Builds or destroys all scrollbar DOM elements (scrollbar, track, handle)
         * @param destroy Indicates whether the DOM shall be build or destroyed.
         */
        function setupScrollbarsDOM(destroy) {
          var selectOrGenerateScrollbarDOM = function (isHorizontal) {
            var scrollbarClassName = isHorizontal
              ? _classNameScrollbarHorizontal
              : _classNameScrollbarVertical;
            var scrollbar = selectOrGenerateDivByClass(
              _classNameScrollbar + _strSpace + scrollbarClassName,
              true
            );
            var track = selectOrGenerateDivByClass(
              _classNameScrollbarTrack,
              scrollbar
            );
            var handle = selectOrGenerateDivByClass(
              _classNameScrollbarHandle,
              scrollbar
            );

            if (!_domExists && !destroy) {
              scrollbar.append(track);
              track.append(handle);
            }

            return {
              _scrollbar: scrollbar,
              _track: track,
              _handle: handle,
            };
          };
          function resetScrollbarDOM(isHorizontal) {
            var scrollbarVars = getScrollbarVars(isHorizontal);
            var scrollbar = scrollbarVars._scrollbar;
            var track = scrollbarVars._track;
            var handle = scrollbarVars._handle;

            if (_domExists && _initialized) {
              each([scrollbar, track, handle], function (i, elm) {
                removeClass(
                  elm.removeAttr(LEXICON.s),
                  _classNamesDynamicDestroy
                );
              });
            } else {
              remove(
                scrollbar ||
                  selectOrGenerateScrollbarDOM(isHorizontal)._scrollbar
              );
            }
          }
          var horizontalElements;
          var verticalElements;

          if (!destroy) {
            horizontalElements = selectOrGenerateScrollbarDOM(true);
            verticalElements = selectOrGenerateScrollbarDOM();

            _scrollbarHorizontalElement = horizontalElements._scrollbar;
            _scrollbarHorizontalTrackElement = horizontalElements._track;
            _scrollbarHorizontalHandleElement = horizontalElements._handle;
            _scrollbarVerticalElement = verticalElements._scrollbar;
            _scrollbarVerticalTrackElement = verticalElements._track;
            _scrollbarVerticalHandleElement = verticalElements._handle;

            if (!_domExists) {
              _paddingElement.after(_scrollbarVerticalElement);
              _paddingElement.after(_scrollbarHorizontalElement);
            }
          } else {
            resetScrollbarDOM(true);
            resetScrollbarDOM();
          }
        }

        /**
         * Initializes all scrollbar interactivity events. (track and handle dragging, clicking, scrolling)
         * @param isHorizontal True if the target scrollbar is the horizontal scrollbar, false if the target scrollbar is the vertical scrollbar.
         */
        function setupScrollbarEvents(isHorizontal) {
          var scrollbarVars = getScrollbarVars(isHorizontal);
          var scrollbarVarsInfo = scrollbarVars._info;
          var insideIFrame = _windowElementNative.top !== _windowElementNative;
          var xy = scrollbarVars._x_y;
          var XY = scrollbarVars._X_Y;
          var scroll = _strScroll + scrollbarVars._Left_Top;
          var strActive = "active";
          var strSnapHandle = "snapHandle";
          var strClickEvent = "click";
          var scrollDurationFactor = 1;
          var increaseDecreaseScrollAmountKeyCodes = [16, 17]; //shift, ctrl
          var trackTimeout;
          var mouseDownScroll;
          var mouseDownOffset;
          var mouseDownInvertedScale;

          function getPointerPosition(event) {
            return _msieVersion && insideIFrame
              ? event["screen" + XY]
              : COMPATIBILITY.page(event)[xy]; //use screen coordinates in EDGE & IE because the page values are incorrect in frames.
          }
          function getPreparedScrollbarsOption(name) {
            return _currentPreparedOptions.scrollbars[name];
          }
          function increaseTrackScrollAmount() {
            scrollDurationFactor = 0.5;
          }
          function decreaseTrackScrollAmount() {
            scrollDurationFactor = 1;
          }
          function stopClickEventPropagation(event) {
            COMPATIBILITY.stpP(event);
          }
          function documentKeyDown(event) {
            if (
              inArray(event.keyCode, increaseDecreaseScrollAmountKeyCodes) > -1
            )
              increaseTrackScrollAmount();
          }
          function documentKeyUp(event) {
            if (
              inArray(event.keyCode, increaseDecreaseScrollAmountKeyCodes) > -1
            )
              decreaseTrackScrollAmount();
          }
          function onMouseTouchDownContinue(event) {
            var originalEvent = event.originalEvent || event;
            var isTouchEvent = originalEvent.touches !== undefined;
            return _sleeping ||
              _destroyed ||
              nativeOverlayScrollbarsAreActive() ||
              !_scrollbarsDragScrollingCache ||
              (isTouchEvent && !getPreparedScrollbarsOption("touchSupport"))
              ? false
              : COMPATIBILITY.mBtn(event) === 1 || isTouchEvent;
          }
          function documentDragMove(event) {
            if (onMouseTouchDownContinue(event)) {
              var trackLength = scrollbarVarsInfo._trackLength;
              var handleLength = scrollbarVarsInfo._handleLength;
              var scrollRange = scrollbarVarsInfo._maxScroll;
              var scrollRaw =
                (getPointerPosition(event) - mouseDownOffset) *
                mouseDownInvertedScale;
              var scrollDeltaPercent = scrollRaw / (trackLength - handleLength);
              var scrollDelta = scrollRange * scrollDeltaPercent;
              scrollDelta = isFinite(scrollDelta) ? scrollDelta : 0;
              if (_isRTL && isHorizontal && !_rtlScrollBehavior.i)
                scrollDelta *= -1;

              _viewportElement[scroll](
                MATH.round(mouseDownScroll + scrollDelta)
              );

              if (_scrollbarsHandlesDefineScrollPos)
                refreshScrollbarHandleOffset(
                  isHorizontal,
                  mouseDownScroll + scrollDelta
                );

              if (!_supportPassiveEvents) COMPATIBILITY.prvD(event);
            } else documentMouseTouchUp(event);
          }
          function documentMouseTouchUp(event) {
            event = event || event.originalEvent;

            setupResponsiveEventListener(
              _documentElement,
              [
                _strMouseTouchMoveEvent,
                _strMouseTouchUpEvent,
                _strKeyDownEvent,
                _strKeyUpEvent,
                _strSelectStartEvent,
              ],
              [
                documentDragMove,
                documentMouseTouchUp,
                documentKeyDown,
                documentKeyUp,
                documentOnSelectStart,
              ],
              true
            );
            COMPATIBILITY.rAF()(function () {
              setupResponsiveEventListener(
                _documentElement,
                strClickEvent,
                stopClickEventPropagation,
                true,
                { _capture: true }
              );
            });

            if (_scrollbarsHandlesDefineScrollPos)
              refreshScrollbarHandleOffset(isHorizontal, true);

            _scrollbarsHandlesDefineScrollPos = false;
            removeClass(_bodyElement, _classNameDragging);
            removeClass(scrollbarVars._handle, strActive);
            removeClass(scrollbarVars._track, strActive);
            removeClass(scrollbarVars._scrollbar, strActive);

            mouseDownScroll = undefined;
            mouseDownOffset = undefined;
            mouseDownInvertedScale = 1;

            decreaseTrackScrollAmount();

            if (trackTimeout !== undefined) {
              _base.scrollStop();
              clearTimeout(trackTimeout);
              trackTimeout = undefined;
            }

            if (event) {
              var rect = _hostElementNative[LEXICON.bCR]();
              var mouseInsideHost =
                event.clientX >= rect.left &&
                event.clientX <= rect.right &&
                event.clientY >= rect.top &&
                event.clientY <= rect.bottom;

              //if mouse is outside host element
              if (!mouseInsideHost) hostOnMouseLeave();

              if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)
                refreshScrollbarsAutoHide(false);
            }
          }
          function onHandleMouseTouchDown(event) {
            if (onMouseTouchDownContinue(event))
              onHandleMouseTouchDownAction(event);
          }
          function onHandleMouseTouchDownAction(event) {
            mouseDownScroll = _viewportElement[scroll]();
            mouseDownScroll = isNaN(mouseDownScroll) ? 0 : mouseDownScroll;
            if ((_isRTL && isHorizontal && !_rtlScrollBehavior.n) || !_isRTL)
              mouseDownScroll = mouseDownScroll < 0 ? 0 : mouseDownScroll;

            mouseDownInvertedScale = getHostElementInvertedScale()[xy];
            mouseDownOffset = getPointerPosition(event);

            _scrollbarsHandlesDefineScrollPos =
              !getPreparedScrollbarsOption(strSnapHandle);
            addClass(_bodyElement, _classNameDragging);
            addClass(scrollbarVars._handle, strActive);
            addClass(scrollbarVars._scrollbar, strActive);

            setupResponsiveEventListener(
              _documentElement,
              [
                _strMouseTouchMoveEvent,
                _strMouseTouchUpEvent,
                _strSelectStartEvent,
              ],
              [documentDragMove, documentMouseTouchUp, documentOnSelectStart]
            );
            COMPATIBILITY.rAF()(function () {
              setupResponsiveEventListener(
                _documentElement,
                strClickEvent,
                stopClickEventPropagation,
                false,
                { _capture: true }
              );
            });

            if (_msieVersion || !_documentMixed) COMPATIBILITY.prvD(event);
            COMPATIBILITY.stpP(event);
          }
          function onTrackMouseTouchDown(event) {
            if (onMouseTouchDownContinue(event)) {
              var handleToViewportRatio =
                scrollbarVars._info._handleLength /
                Math.round(
                  MATH.min(
                    1,
                    _viewportSize[scrollbarVars._w_h] /
                      _contentScrollSizeCache[scrollbarVars._w_h]
                  ) * scrollbarVars._info._trackLength
                );
              var scrollDistance = MATH.round(
                _viewportSize[scrollbarVars._w_h] * handleToViewportRatio
              );
              var scrollBaseDuration = 270 * handleToViewportRatio;
              var scrollFirstIterationDelay = 400 * handleToViewportRatio;
              var trackOffset =
                scrollbarVars._track.offset()[scrollbarVars._left_top];
              var ctrlKey = event.ctrlKey;
              var instantScroll = event.shiftKey;
              var instantScrollTransition = instantScroll && ctrlKey;
              var isFirstIteration = true;
              var easing = "linear";
              var decreaseScroll;
              var finishedCondition;
              var scrollActionFinsished = function (transition) {
                if (_scrollbarsHandlesDefineScrollPos)
                  refreshScrollbarHandleOffset(isHorizontal, transition);
              };
              var scrollActionInstantFinished = function () {
                scrollActionFinsished();
                onHandleMouseTouchDownAction(event);
              };
              var scrollAction = function () {
                if (!_destroyed) {
                  var mouseOffset =
                    (mouseDownOffset - trackOffset) * mouseDownInvertedScale;
                  var handleOffset = scrollbarVarsInfo._handleOffset;
                  var trackLength = scrollbarVarsInfo._trackLength;
                  var handleLength = scrollbarVarsInfo._handleLength;
                  var scrollRange = scrollbarVarsInfo._maxScroll;
                  var currScroll = scrollbarVarsInfo._currentScroll;
                  var scrollDuration =
                    scrollBaseDuration * scrollDurationFactor;
                  var timeoutDelay = isFirstIteration
                    ? MATH.max(scrollFirstIterationDelay, scrollDuration)
                    : scrollDuration;
                  var instantScrollPosition =
                    scrollRange *
                    ((mouseOffset - handleLength / 2) /
                      (trackLength - handleLength)); // 100% * positionPercent
                  var rtlIsNormal =
                    _isRTL &&
                    isHorizontal &&
                    ((!_rtlScrollBehavior.i && !_rtlScrollBehavior.n) ||
                      _normalizeRTLCache);
                  var decreaseScrollCondition = rtlIsNormal
                    ? handleOffset < mouseOffset
                    : handleOffset > mouseOffset;
                  var scrollObj = {};
                  var animationObj = {
                    easing: easing,
                    step: function (now) {
                      if (_scrollbarsHandlesDefineScrollPos) {
                        _viewportElement[scroll](now); //https://github.com/jquery/jquery/issues/4340
                        refreshScrollbarHandleOffset(isHorizontal, now);
                      }
                    },
                  };
                  instantScrollPosition = isFinite(instantScrollPosition)
                    ? instantScrollPosition
                    : 0;
                  instantScrollPosition =
                    _isRTL && isHorizontal && !_rtlScrollBehavior.i
                      ? scrollRange - instantScrollPosition
                      : instantScrollPosition;

                  //_base.scrollStop();

                  if (instantScroll) {
                    _viewportElement[scroll](instantScrollPosition); //scroll instantly to new position
                    if (instantScrollTransition) {
                      //get the scroll position after instant scroll (in case CSS Snap Points are used) to get the correct snapped scroll position
                      //and the animation stops at the correct point
                      instantScrollPosition = _viewportElement[scroll]();
                      //scroll back to the position before instant scrolling so animation can be performed
                      _viewportElement[scroll](currScroll);

                      instantScrollPosition =
                        rtlIsNormal && _rtlScrollBehavior.i
                          ? scrollRange - instantScrollPosition
                          : instantScrollPosition;
                      instantScrollPosition =
                        rtlIsNormal && _rtlScrollBehavior.n
                          ? -instantScrollPosition
                          : instantScrollPosition;

                      scrollObj[xy] = instantScrollPosition;
                      _base.scroll(
                        scrollObj,
                        extendDeep(animationObj, {
                          duration: 130,
                          complete: scrollActionInstantFinished,
                        })
                      );
                    } else scrollActionInstantFinished();
                  } else {
                    decreaseScroll = isFirstIteration
                      ? decreaseScrollCondition
                      : decreaseScroll;
                    finishedCondition = rtlIsNormal
                      ? decreaseScroll
                        ? handleOffset + handleLength >= mouseOffset
                        : handleOffset <= mouseOffset
                      : decreaseScroll
                      ? handleOffset <= mouseOffset
                      : handleOffset + handleLength >= mouseOffset;

                    if (finishedCondition) {
                      clearTimeout(trackTimeout);
                      _base.scrollStop();
                      trackTimeout = undefined;
                      scrollActionFinsished(true);
                    } else {
                      trackTimeout = setTimeout(scrollAction, timeoutDelay);

                      scrollObj[xy] =
                        (decreaseScroll ? "-=" : "+=") + scrollDistance;
                      _base.scroll(
                        scrollObj,
                        extendDeep(animationObj, {
                          duration: scrollDuration,
                        })
                      );
                    }
                    isFirstIteration = false;
                  }
                }
              };
              if (ctrlKey) increaseTrackScrollAmount();

              mouseDownInvertedScale = getHostElementInvertedScale()[xy];
              mouseDownOffset = COMPATIBILITY.page(event)[xy];

              _scrollbarsHandlesDefineScrollPos =
                !getPreparedScrollbarsOption(strSnapHandle);
              addClass(_bodyElement, _classNameDragging);
              addClass(scrollbarVars._track, strActive);
              addClass(scrollbarVars._scrollbar, strActive);

              setupResponsiveEventListener(
                _documentElement,
                [
                  _strMouseTouchUpEvent,
                  _strKeyDownEvent,
                  _strKeyUpEvent,
                  _strSelectStartEvent,
                ],
                [
                  documentMouseTouchUp,
                  documentKeyDown,
                  documentKeyUp,
                  documentOnSelectStart,
                ]
              );

              scrollAction();
              COMPATIBILITY.prvD(event);
              COMPATIBILITY.stpP(event);
            }
          }
          function onTrackMouseTouchEnter(event) {
            //make sure both scrollbars will stay visible if one scrollbar is hovered if autoHide is "scroll" or "move".
            _scrollbarsHandleHovered = true;
            if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)
              refreshScrollbarsAutoHide(true);
          }
          function onTrackMouseTouchLeave(event) {
            _scrollbarsHandleHovered = false;
            if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)
              refreshScrollbarsAutoHide(false);
          }
          function onScrollbarMouseTouchDown(event) {
            COMPATIBILITY.stpP(event);
          }

          addDestroyEventListener(
            scrollbarVars._handle,
            _strMouseTouchDownEvent,
            onHandleMouseTouchDown
          );
          addDestroyEventListener(
            scrollbarVars._track,
            [_strMouseTouchDownEvent, _strMouseEnter, _strMouseLeave],
            [
              onTrackMouseTouchDown,
              onTrackMouseTouchEnter,
              onTrackMouseTouchLeave,
            ]
          );
          addDestroyEventListener(
            scrollbarVars._scrollbar,
            _strMouseTouchDownEvent,
            onScrollbarMouseTouchDown
          );

          if (_supportTransition) {
            addDestroyEventListener(
              scrollbarVars._scrollbar,
              _strTransitionEndEvent,
              function (event) {
                if (event.target !== scrollbarVars._scrollbar[0]) return;
                refreshScrollbarHandleLength(isHorizontal);
                refreshScrollbarHandleOffset(isHorizontal);
              }
            );
          }
        }

        /**
         * Shows or hides the given scrollbar and applied a class name which indicates if the scrollbar is scrollable or not.
         * @param isHorizontal True if the horizontal scrollbar is the target, false if the vertical scrollbar is the target.
         * @param shallBeVisible True if the scrollbar shall be shown, false if hidden.
         * @param canScroll True if the scrollbar is scrollable, false otherwise.
         */
        function refreshScrollbarAppearance(
          isHorizontal,
          shallBeVisible,
          canScroll
        ) {
          var scrollbarHiddenClassName = isHorizontal
            ? _classNameHostScrollbarHorizontalHidden
            : _classNameHostScrollbarVerticalHidden;
          var scrollbarElement = isHorizontal
            ? _scrollbarHorizontalElement
            : _scrollbarVerticalElement;

          addRemoveClass(
            _hostElement,
            scrollbarHiddenClassName,
            !shallBeVisible
          );
          addRemoveClass(
            scrollbarElement,
            _classNameScrollbarUnusable,
            !canScroll
          );
        }

        /**
         * Autoshows / autohides both scrollbars with.
         * @param shallBeVisible True if the scrollbars shall be autoshown (only the case if they are hidden by a autohide), false if the shall be auto hidden.
         * @param delayfree True if the scrollbars shall be hidden without a delay, false or undefined otherwise.
         */
        function refreshScrollbarsAutoHide(shallBeVisible, delayfree) {
          clearTimeout(_scrollbarsAutoHideTimeoutId);
          if (shallBeVisible) {
            //if(_hasOverflowCache.x && _hideOverflowCache.xs)
            removeClass(
              _scrollbarHorizontalElement,
              _classNameScrollbarAutoHidden
            );
            //if(_hasOverflowCache.y && _hideOverflowCache.ys)
            removeClass(
              _scrollbarVerticalElement,
              _classNameScrollbarAutoHidden
            );
          } else {
            var anyActive;
            var strActive = "active";
            var hide = function () {
              if (!_scrollbarsHandleHovered && !_destroyed) {
                anyActive =
                  _scrollbarHorizontalHandleElement.hasClass(strActive) ||
                  _scrollbarVerticalHandleElement.hasClass(strActive);
                if (
                  !anyActive &&
                  (_scrollbarsAutoHideScroll ||
                    _scrollbarsAutoHideMove ||
                    _scrollbarsAutoHideLeave)
                )
                  addClass(
                    _scrollbarHorizontalElement,
                    _classNameScrollbarAutoHidden
                  );
                if (
                  !anyActive &&
                  (_scrollbarsAutoHideScroll ||
                    _scrollbarsAutoHideMove ||
                    _scrollbarsAutoHideLeave)
                )
                  addClass(
                    _scrollbarVerticalElement,
                    _classNameScrollbarAutoHidden
                  );
              }
            };
            if (_scrollbarsAutoHideDelay > 0 && delayfree !== true)
              _scrollbarsAutoHideTimeoutId = setTimeout(
                hide,
                _scrollbarsAutoHideDelay
              );
            else hide();
          }
        }

        /**
         * Refreshes the handle length of the given scrollbar.
         * @param isHorizontal True if the horizontal scrollbar handle shall be refreshed, false if the vertical one shall be refreshed.
         */
        function refreshScrollbarHandleLength(isHorizontal) {
          var handleCSS = {};
          var scrollbarVars = getScrollbarVars(isHorizontal);
          var scrollbarVarsInfo = scrollbarVars._info;
          var digit = 1000000;
          //get and apply intended handle length
          var handleRatio = MATH.min(
            1,
            _viewportSize[scrollbarVars._w_h] /
              _contentScrollSizeCache[scrollbarVars._w_h]
          );
          handleCSS[scrollbarVars._width_height] =
            MATH.floor(handleRatio * 100 * digit) / digit + "%"; //the last * digit / digit is for flooring to the 4th digit

          if (!nativeOverlayScrollbarsAreActive())
            scrollbarVars._handle.css(handleCSS);

          //measure the handle length to respect min & max length
          scrollbarVarsInfo._handleLength =
            scrollbarVars._handle[0]["offset" + scrollbarVars._Width_Height];
          scrollbarVarsInfo._handleLengthRatio = handleRatio;
        }

        /**
         * Refreshes the handle offset of the given scrollbar.
         * @param isHorizontal True if the horizontal scrollbar handle shall be refreshed, false if the vertical one shall be refreshed.
         * @param scrollOrTransition The scroll position of the given scrollbar axis to which the handle shall be moved or a boolean which indicates whether a transition shall be applied. If undefined or boolean if the current scroll-offset is taken. (if isHorizontal ? scrollLeft : scrollTop)
         */
        function refreshScrollbarHandleOffset(
          isHorizontal,
          scrollOrTransition
        ) {
          var transition = type(scrollOrTransition) == TYPES.b;
          var transitionDuration = 250;
          var isRTLisHorizontal = _isRTL && isHorizontal;
          var scrollbarVars = getScrollbarVars(isHorizontal);
          var scrollbarVarsInfo = scrollbarVars._info;
          var strTranslateBrace = "translate(";
          var strTransform = VENDORS._cssProperty("transform");
          var strTransition = VENDORS._cssProperty("transition");
          var nativeScroll = isHorizontal
            ? _viewportElement[_strScrollLeft]()
            : _viewportElement[_strScrollTop]();
          var currentScroll =
            scrollOrTransition === undefined || transition
              ? nativeScroll
              : scrollOrTransition;

          //measure the handle length to respect min & max length
          var handleLength = scrollbarVarsInfo._handleLength;
          var trackLength =
            scrollbarVars._track[0]["offset" + scrollbarVars._Width_Height];
          var handleTrackDiff = trackLength - handleLength;
          var handleCSS = {};
          var transformOffset;
          var translateValue;

          //DONT use the variable '_contentScrollSizeCache[scrollbarVars._w_h]' instead of '_viewportElement[0]['scroll' + scrollbarVars._Width_Height]'
          // because its a bit behind during the small delay when content size updates
          //(delay = mutationObserverContentLag, if its 0 then this var could be used)
          var maxScroll =
            (_viewportElementNative[_strScroll + scrollbarVars._Width_Height] -
              _viewportElementNative["client" + scrollbarVars._Width_Height]) *
            (_rtlScrollBehavior.n && isRTLisHorizontal ? -1 : 1); //* -1 if rtl scroll max is negative
          var getScrollRatio = function (base) {
            return isNaN(base / maxScroll)
              ? 0
              : MATH.max(0, MATH.min(1, base / maxScroll));
          };
          var getHandleOffset = function (scrollRatio) {
            var offset = handleTrackDiff * scrollRatio;
            offset = isNaN(offset) ? 0 : offset;
            offset =
              isRTLisHorizontal && !_rtlScrollBehavior.i
                ? trackLength - handleLength - offset
                : offset;
            offset = MATH.max(0, offset);
            return offset;
          };
          var scrollRatio = getScrollRatio(nativeScroll);
          var unsnappedScrollRatio = getScrollRatio(currentScroll);
          var handleOffset = getHandleOffset(unsnappedScrollRatio);
          var snappedHandleOffset = getHandleOffset(scrollRatio);

          scrollbarVarsInfo._maxScroll = maxScroll;
          scrollbarVarsInfo._currentScroll = nativeScroll;
          scrollbarVarsInfo._currentScrollRatio = scrollRatio;

          if (_supportTransform) {
            transformOffset = isRTLisHorizontal
              ? -(trackLength - handleLength - handleOffset)
              : handleOffset; //in px
            //transformOffset = (transformOffset / trackLength * 100) * (trackLength / handleLength); //in %
            translateValue = isHorizontal
              ? strTranslateBrace + transformOffset + "px, 0)"
              : strTranslateBrace + "0, " + transformOffset + "px)";

            handleCSS[strTransform] = translateValue;

            //apply or clear up transition
            if (_supportTransition)
              handleCSS[strTransition] =
                transition &&
                MATH.abs(handleOffset - scrollbarVarsInfo._handleOffset) > 1
                  ? getCSSTransitionString(scrollbarVars._handle) +
                    ", " +
                    (strTransform + _strSpace + transitionDuration + "ms")
                  : _strEmpty;
          } else handleCSS[scrollbarVars._left_top] = handleOffset;

          //only apply css if offset has changed and overflow exists.
          if (!nativeOverlayScrollbarsAreActive()) {
            scrollbarVars._handle.css(handleCSS);

            //clear up transition
            if (_supportTransform && _supportTransition && transition) {
              scrollbarVars._handle.one(_strTransitionEndEvent, function () {
                if (!_destroyed)
                  scrollbarVars._handle.css(strTransition, _strEmpty);
              });
            }
          }

          scrollbarVarsInfo._handleOffset = handleOffset;
          scrollbarVarsInfo._snappedHandleOffset = snappedHandleOffset;
          scrollbarVarsInfo._trackLength = trackLength;
        }

        /**
         * Refreshes the interactivity of the given scrollbar element.
         * @param isTrack True if the track element is the target, false if the handle element is the target.
         * @param value True for interactivity false for no interactivity.
         */
        function refreshScrollbarsInteractive(isTrack, value) {
          var action = value ? "removeClass" : "addClass";
          var element1 = isTrack
            ? _scrollbarHorizontalTrackElement
            : _scrollbarHorizontalHandleElement;
          var element2 = isTrack
            ? _scrollbarVerticalTrackElement
            : _scrollbarVerticalHandleElement;
          var className = isTrack
            ? _classNameScrollbarTrackOff
            : _classNameScrollbarHandleOff;

          element1[action](className);
          element2[action](className);
        }

        /**
         * Returns a object which is used for fast access for specific variables.
         * @param isHorizontal True if the horizontal scrollbar vars shall be accessed, false if the vertical scrollbar vars shall be accessed.
         * @returns {{wh: string, WH: string, lt: string, _wh: string, _lt: string, t: *, h: *, c: {}, s: *}}
         */
        function getScrollbarVars(isHorizontal) {
          return {
            _width_height: isHorizontal ? _strWidth : _strHeight,
            _Width_Height: isHorizontal ? "Width" : "Height",
            _left_top: isHorizontal ? _strLeft : _strTop,
            _Left_Top: isHorizontal ? "Left" : "Top",
            _x_y: isHorizontal ? _strX : _strY,
            _X_Y: isHorizontal ? "X" : "Y",
            _w_h: isHorizontal ? "w" : "h",
            _l_t: isHorizontal ? "l" : "t",
            _track: isHorizontal
              ? _scrollbarHorizontalTrackElement
              : _scrollbarVerticalTrackElement,
            _handle: isHorizontal
              ? _scrollbarHorizontalHandleElement
              : _scrollbarVerticalHandleElement,
            _scrollbar: isHorizontal
              ? _scrollbarHorizontalElement
              : _scrollbarVerticalElement,
            _info: isHorizontal ? _scrollHorizontalInfo : _scrollVerticalInfo,
          };
        }

        //==== Scrollbar Corner ====//

        /**
         * Builds or destroys the scrollbar corner DOM element.
         * @param destroy Indicates whether the DOM shall be build or destroyed.
         */
        function setupScrollbarCornerDOM(destroy) {
          _scrollbarCornerElement =
            _scrollbarCornerElement ||
            selectOrGenerateDivByClass(_classNameScrollbarCorner, true);

          if (!destroy) {
            if (!_domExists) {
              _hostElement.append(_scrollbarCornerElement);
            }
          } else {
            if (_domExists && _initialized) {
              removeClass(
                _scrollbarCornerElement.removeAttr(LEXICON.s),
                _classNamesDynamicDestroy
              );
            } else {
              remove(_scrollbarCornerElement);
            }
          }
        }

        /**
         * Initializes all scrollbar corner interactivity events.
         */
        function setupScrollbarCornerEvents() {
          var insideIFrame = _windowElementNative.top !== _windowElementNative;
          var mouseDownPosition = {};
          var mouseDownSize = {};
          var mouseDownInvertedScale = {};
          var reconnectMutationObserver;

          function documentDragMove(event) {
            if (onMouseTouchDownContinue(event)) {
              var pageOffset = getCoordinates(event);
              var hostElementCSS = {};
              if (_resizeHorizontal || _resizeBoth)
                hostElementCSS[_strWidth] =
                  mouseDownSize.w +
                  (pageOffset.x - mouseDownPosition.x) *
                    mouseDownInvertedScale.x;
              if (_resizeVertical || _resizeBoth)
                hostElementCSS[_strHeight] =
                  mouseDownSize.h +
                  (pageOffset.y - mouseDownPosition.y) *
                    mouseDownInvertedScale.y;
              _hostElement.css(hostElementCSS);
              COMPATIBILITY.stpP(event);
            } else {
              documentMouseTouchUp(event);
            }
          }
          function documentMouseTouchUp(event) {
            var eventIsTrusted = event !== undefined;

            setupResponsiveEventListener(
              _documentElement,
              [
                _strSelectStartEvent,
                _strMouseTouchMoveEvent,
                _strMouseTouchUpEvent,
              ],
              [documentOnSelectStart, documentDragMove, documentMouseTouchUp],
              true
            );

            removeClass(_bodyElement, _classNameDragging);
            if (_scrollbarCornerElement.releaseCapture)
              _scrollbarCornerElement.releaseCapture();

            if (eventIsTrusted) {
              if (reconnectMutationObserver) connectMutationObservers();
              _base.update(_strAuto);
            }
            reconnectMutationObserver = false;
          }
          function onMouseTouchDownContinue(event) {
            var originalEvent = event.originalEvent || event;
            var isTouchEvent = originalEvent.touches !== undefined;
            return _sleeping || _destroyed
              ? false
              : COMPATIBILITY.mBtn(event) === 1 || isTouchEvent;
          }
          function getCoordinates(event) {
            return _msieVersion && insideIFrame
              ? { x: event.screenX, y: event.screenY }
              : COMPATIBILITY.page(event);
          }

          addDestroyEventListener(
            _scrollbarCornerElement,
            _strMouseTouchDownEvent,
            function (event) {
              if (onMouseTouchDownContinue(event) && !_resizeNone) {
                if (_mutationObserversConnected) {
                  reconnectMutationObserver = true;
                  disconnectMutationObservers();
                }

                mouseDownPosition = getCoordinates(event);

                mouseDownSize.w =
                  _hostElementNative[LEXICON.oW] -
                  (!_isBorderBox ? _paddingX : 0);
                mouseDownSize.h =
                  _hostElementNative[LEXICON.oH] -
                  (!_isBorderBox ? _paddingY : 0);
                mouseDownInvertedScale = getHostElementInvertedScale();

                setupResponsiveEventListener(
                  _documentElement,
                  [
                    _strSelectStartEvent,
                    _strMouseTouchMoveEvent,
                    _strMouseTouchUpEvent,
                  ],
                  [
                    documentOnSelectStart,
                    documentDragMove,
                    documentMouseTouchUp,
                  ]
                );

                addClass(_bodyElement, _classNameDragging);
                if (_scrollbarCornerElement.setCapture)
                  _scrollbarCornerElement.setCapture();

                COMPATIBILITY.prvD(event);
                COMPATIBILITY.stpP(event);
              }
            }
          );
        }

        //==== Utils ====//

        /**
         * Calls the callback with the given name. The Context of this callback is always _base (this).
         * @param name The name of the target which shall be called.
         * @param args The args with which the callback shall be called.
         * @param dependent Boolean which decides whether the callback shall be fired, undefined is like a "true" value.
         */
        function dispatchCallback(name, args, dependent) {
          if (dependent === false) return;
          if (_initialized) {
            var callback = _currentPreparedOptions.callbacks[name];
            var extensionOnName = name;
            var ext;

            if (extensionOnName.substr(0, 2) === "on")
              extensionOnName =
                extensionOnName.substr(2, 1).toLowerCase() +
                extensionOnName.substr(3);

            if (type(callback) == TYPES.f) callback.call(_base, args);

            each(_extensions, function () {
              ext = this;
              if (type(ext.on) == TYPES.f) ext.on(extensionOnName, args);
            });
          } else if (!_destroyed)
            _callbacksInitQeueue.push({ n: name, a: args });
        }

        /**
         * Sets the "top, right, bottom, left" properties, with a given prefix, of the given css object.
         * @param targetCSSObject The css object to which the values shall be applied.
         * @param prefix The prefix of the "top, right, bottom, left" css properties. (example: 'padding-' is a valid prefix)
         * @param values A array of values which shall be applied to the "top, right, bottom, left" -properties. The array order is [top, right, bottom, left].
         * If this argument is undefined the value '' (empty string) will be applied to all properties.
         */
        function setTopRightBottomLeft(targetCSSObject, prefix, values) {
          prefix = prefix || _strEmpty;
          values = values || [_strEmpty, _strEmpty, _strEmpty, _strEmpty];

          targetCSSObject[prefix + _strTop] = values[0];
          targetCSSObject[prefix + _strRight] = values[1];
          targetCSSObject[prefix + _strBottom] = values[2];
          targetCSSObject[prefix + _strLeft] = values[3];
        }

        /**
         * Gets the "top, right, bottom, left" CSS properties of the CSS property with the given prefix from the host element.
         * @param prefix The prefix of the "top, right, bottom, left" css properties. (example: 'padding-' is a valid prefix)
         * @param suffix The suffix of the "top, right, bottom, left" css properties. (example: 'border-' is a valid prefix with '-width' is a valid suffix)
         * @param zeroX True if the x axis shall be 0.
         * @param zeroY True if the y axis shall be 0.
         * @returns {{}} The object which contains the numbers of the read CSS properties.
         */
        function getTopRightBottomLeftHost(prefix, suffix, zeroX, zeroY) {
          suffix = suffix || _strEmpty;
          prefix = prefix || _strEmpty;
          return {
            t: zeroY
              ? 0
              : parseToZeroOrNumber(
                  _hostElement.css(prefix + _strTop + suffix)
                ),
            r: zeroX
              ? 0
              : parseToZeroOrNumber(
                  _hostElement.css(prefix + _strRight + suffix)
                ),
            b: zeroY
              ? 0
              : parseToZeroOrNumber(
                  _hostElement.css(prefix + _strBottom + suffix)
                ),
            l: zeroX
              ? 0
              : parseToZeroOrNumber(
                  _hostElement.css(prefix + _strLeft + suffix)
                ),
          };
        }

        /**
         * Returns the computed CSS transition string from the given element.
         * @param element The element from which the transition string shall be returned.
         * @returns {string} The CSS transition string from the given element.
         */
        function getCSSTransitionString(element) {
          var transitionStr = VENDORS._cssProperty("transition");
          var assembledValue = element.css(transitionStr);
          if (assembledValue) return assembledValue;
          var regExpString = "\\s*(" + "([^,(]+(\\(.+?\\))?)+" + ")[\\s,]*";
          var regExpMain = new RegExp(regExpString);
          var regExpValidate = new RegExp("^(" + regExpString + ")+$");
          var properties = "property duration timing-function delay".split(" ");
          var result = [];
          var strResult;
          var valueArray;
          var i = 0;
          var j;
          var splitCssStyleByComma = function (str) {
            strResult = [];
            if (!str.match(regExpValidate)) return str;
            while (str.match(regExpMain)) {
              strResult.push(RegExp.$1);
              str = str.replace(regExpMain, _strEmpty);
            }

            return strResult;
          };
          for (; i < properties[LEXICON.l]; i++) {
            valueArray = splitCssStyleByComma(
              element.css(transitionStr + "-" + properties[i])
            );
            for (j = 0; j < valueArray[LEXICON.l]; j++)
              result[j] =
                (result[j] ? result[j] + _strSpace : _strEmpty) + valueArray[j];
          }
          return result.join(", ");
        }

        /**
         * Generates a Regular Expression which matches with a string which starts with 'os-host'.
         * @param {boolean} withCurrClassNameOption The Regular Expression also matches if the string is the current ClassName option (multiple values splitted by space possible).
         * @param {boolean} withOldClassNameOption The Regular Expression also matches if the string is the old ClassName option (multiple values splitted by space possible).
         */
        function createHostClassNameRegExp(
          withCurrClassNameOption,
          withOldClassNameOption
        ) {
          var i;
          var split;
          var appendix;
          var appendClasses = function (classes, condition) {
            appendix = "";
            if (condition && typeof classes == TYPES.s) {
              split = classes.split(_strSpace);
              for (i = 0; i < split[LEXICON.l]; i++)
                appendix += "|" + split[i] + "$";
              // split[i].replace(/[.*+?^${}()|[\]\\]/g, '\\$&') for escaping regex characters
            }
            return appendix;
          };

          return new RegExp(
            "(^" +
              _classNameHostElement +
              "([-_].+|)$)" +
              appendClasses(_classNameCache, withCurrClassNameOption) +
              appendClasses(_oldClassName, withOldClassNameOption),
            "g"
          );
        }

        /**
         * Calculates the host-elements inverted scale. (invertedScale = 1 / scale)
         * @returns {{x: number, y: number}} The scale of the host-element.
         */
        function getHostElementInvertedScale() {
          var rect = _paddingElementNative[LEXICON.bCR]();
          return {
            x: _supportTransform
              ? 1 /
                  (MATH.round(rect.width) /
                    _paddingElementNative[LEXICON.oW]) || 1
              : 1,
            y: _supportTransform
              ? 1 /
                  (MATH.round(rect.height) /
                    _paddingElementNative[LEXICON.oH]) || 1
              : 1,
          };
        }

        /**
         * Checks whether the given object is a HTMLElement.
         * @param o The object which shall be checked.
         * @returns {boolean} True the given object is a HTMLElement, false otherwise.
         */
        function isHTMLElement(o) {
          var strOwnerDocument = "ownerDocument";
          var strHTMLElement = "HTMLElement";
          var wnd =
            o && o[strOwnerDocument]
              ? o[strOwnerDocument].parentWindow || window
              : window;
          return typeof wnd[strHTMLElement] == TYPES.o
            ? o instanceof wnd[strHTMLElement] //DOM2
            : o &&
                typeof o == TYPES.o &&
                o !== null &&
                o.nodeType === 1 &&
                typeof o.nodeName == TYPES.s;
        }

        /**
         * Compares 2 arrays and returns the differences between them as a array.
         * @param a1 The first array which shall be compared.
         * @param a2 The second array which shall be compared.
         * @returns {Array} The differences between the two arrays.
         */
        function getArrayDifferences(a1, a2) {
          var a = [];
          var diff = [];
          var i;
          var k;
          for (i = 0; i < a1.length; i++) a[a1[i]] = true;
          for (i = 0; i < a2.length; i++) {
            if (a[a2[i]]) delete a[a2[i]];
            else a[a2[i]] = true;
          }
          for (k in a) diff.push(k);
          return diff;
        }

        /**
         * Returns Zero or the number to which the value can be parsed.
         * @param value The value which shall be parsed.
         * @param toFloat Indicates whether the number shall be parsed to a float.
         */
        function parseToZeroOrNumber(value, toFloat) {
          var num = toFloat ? parseFloat(value) : parseInt(value, 10);
          return isNaN(num) ? 0 : num;
        }

        /**
         * Gets several information of the textarea and returns them as a object or undefined if the browser doesn't support it.
         * @returns {{cursorRow: Number, cursorCol, rows: Number, cols: number, wRow: number, pos: number, max : number}} or undefined if not supported.
         */
        function getTextareaInfo() {
          //read needed values
          var textareaCursorPosition = _targetElementNative.selectionStart;
          if (textareaCursorPosition === undefined) return;

          var textareaValue = _targetElement.val();
          var textareaLength = textareaValue[LEXICON.l];
          var textareaRowSplit = textareaValue.split("\n");
          var textareaLastRow = textareaRowSplit[LEXICON.l];
          var textareaCurrentCursorRowSplit = textareaValue
            .substr(0, textareaCursorPosition)
            .split("\n");
          var widestRow = 0;
          var textareaLastCol = 0;
          var cursorRow = textareaCurrentCursorRowSplit[LEXICON.l];
          var cursorCol =
            textareaCurrentCursorRowSplit[
              textareaCurrentCursorRowSplit[LEXICON.l] - 1
            ][LEXICON.l];
          var rowCols;
          var i;

          //get widest Row and the last column of the textarea
          for (i = 0; i < textareaRowSplit[LEXICON.l]; i++) {
            rowCols = textareaRowSplit[i][LEXICON.l];
            if (rowCols > textareaLastCol) {
              widestRow = i + 1;
              textareaLastCol = rowCols;
            }
          }

          return {
            _cursorRow: cursorRow, //cursorRow
            _cursorColumn: cursorCol, //cursorCol
            _rows: textareaLastRow, //rows
            _columns: textareaLastCol, //cols
            _widestRow: widestRow, //wRow
            _cursorPosition: textareaCursorPosition, //pos
            _cursorMax: textareaLength, //max
          };
        }

        /**
         * Determines whether native overlay scrollbars are active.
         * @returns {boolean} True if native overlay scrollbars are active, false otherwise.
         */
        function nativeOverlayScrollbarsAreActive() {
          return (
            _ignoreOverlayScrollbarHidingCache &&
            _nativeScrollbarIsOverlaid.x &&
            _nativeScrollbarIsOverlaid.y
          );
        }

        /**
         * Gets the element which is used to measure the content size.
         * @returns {*} TextareaCover if target element is textarea else the ContentElement.
         */
        function getContentMeasureElement() {
          return _isTextarea ? _textareaCoverElement[0] : _contentElementNative;
        }

        /**
         * Generates a string which represents a HTML div with the given classes or attributes.
         * @param classesOrAttrs The class of the div as string or a object which represents the attributes of the div. (The class attribute can also be written as "className".)
         * @param content The content of the div as string.
         * @returns {string} The concated string which represents a HTML div and its content.
         */
        function generateDiv(classesOrAttrs, content) {
          return (
            "<div " +
            (classesOrAttrs
              ? type(classesOrAttrs) == TYPES.s
                ? 'class="' + classesOrAttrs + '"'
                : (function () {
                    var key;
                    var attrs = _strEmpty;
                    if (FRAMEWORK.isPlainObject(classesOrAttrs)) {
                      for (key in classesOrAttrs)
                        attrs +=
                          (key === "c" ? "class" : key) +
                          '="' +
                          classesOrAttrs[key] +
                          '" ';
                    }
                    return attrs;
                  })()
              : _strEmpty) +
            ">" +
            (content || _strEmpty) +
            "</div>"
          );
        }

        /**
         * Selects or generates a div with the given class attribute.
         * @param className The class names (divided by spaces) of the div which shall be selected or generated.
         * @param selectParentOrOnlyChildren The parent element from which of the element shall be selected. (if undefined or boolean its hostElement)
         * If its a boolean it decides whether only the children of the host element shall be selected.
         * @returns {*} The generated or selected element.
         */
        function selectOrGenerateDivByClass(
          className,
          selectParentOrOnlyChildren
        ) {
          var onlyChildren = type(selectParentOrOnlyChildren) == TYPES.b;
          var selectParent = onlyChildren
            ? _hostElement
            : selectParentOrOnlyChildren || _hostElement;

          return _domExists && !selectParent[LEXICON.l]
            ? null
            : _domExists
            ? selectParent[onlyChildren ? "children" : "find"](
                _strDot + className.replace(/\s/g, _strDot)
              ).eq(0)
            : FRAMEWORK(generateDiv(className));
        }

        /**
         * Gets the value of the given property from the given object.
         * @param obj The object from which the property value shall be got.
         * @param path The property of which the value shall be got.
         * @returns {*} Returns the value of the searched property or undefined of the property wasn't found.
         */
        function getObjectPropVal(obj, path) {
          var splits = path.split(_strDot);
          var i = 0;
          var val;
          for (; i < splits.length; i++) {
            if (!obj[LEXICON.hOP](splits[i])) return;
            val = obj[splits[i]];
            if (i < splits.length && type(val) == TYPES.o) obj = val;
          }
          return val;
        }

        /**
         * Sets the value of the given property from the given object.
         * @param obj The object from which the property value shall be set.
         * @param path The property of which the value shall be set.
         * @param val The value of the property which shall be set.
         */
        function setObjectPropVal(obj, path, val) {
          var splits = path.split(_strDot);
          var splitsLength = splits.length;
          var i = 0;
          var extendObj = {};
          var extendObjRoot = extendObj;
          for (; i < splitsLength; i++)
            extendObj = extendObj[splits[i]] = i + 1 < splitsLength ? {} : val;
          FRAMEWORK.extend(obj, extendObjRoot, true);
        }

        /**
         * Runs a action for each selector inside the updateOnLoad option.
         * @param {Function} action The action for each updateOnLoad selector, the arguments the function takes is the index and the value (the selector).
         */
        function eachUpdateOnLoad(action) {
          var updateOnLoad = _currentPreparedOptions.updateOnLoad;
          updateOnLoad =
            type(updateOnLoad) == TYPES.s
              ? updateOnLoad.split(_strSpace)
              : updateOnLoad;

          if (COMPATIBILITY.isA(updateOnLoad) && !_destroyed) {
            each(updateOnLoad, action);
          }
        }

        //==== Utils Cache ====//

        /**
         * Compares two values or objects and returns true if they aren't equal.
         * @param current The first value or object which shall be compared.
         * @param cache The second value or object which shall be compared.
         * @param force If true the returned value is always true.
         * @returns {boolean} True if both values or objects aren't equal or force is true, false otherwise.
         */
        function checkCache(current, cache, force) {
          if (force) return force;
          if (type(current) == TYPES.o && type(cache) == TYPES.o) {
            for (var prop in current) {
              if (prop !== "c") {
                if (current[LEXICON.hOP](prop) && cache[LEXICON.hOP](prop)) {
                  if (checkCache(current[prop], cache[prop])) return true;
                } else {
                  return true;
                }
              }
            }
          } else {
            return current !== cache;
          }
          return false;
        }

        //==== Shortcuts ====//

        /**
         * jQuery extend method shortcut with a appended "true" as first argument.
         */
        function extendDeep() {
          return FRAMEWORK.extend.apply(
            this,
            [true].concat([].slice.call(arguments))
          );
        }

        /**
         * jQuery addClass method shortcut.
         */
        function addClass(el, classes) {
          return _frameworkProto.addClass.call(el, classes);
        }

        /**
         * jQuery removeClass method shortcut.
         */
        function removeClass(el, classes) {
          return _frameworkProto.removeClass.call(el, classes);
        }

        /**
         * Adds or removes the given classes dependent on the boolean value. True for add, false for remove.
         */
        function addRemoveClass(el, classes, doAdd) {
          return doAdd ? addClass(el, classes) : removeClass(el, classes);
        }

        /**
         * jQuery remove method shortcut.
         */
        function remove(el) {
          return _frameworkProto.remove.call(el);
        }

        /**
         * Finds the first child element with the given selector of the given element.
         * @param el The root element from which the selector shall be valid.
         * @param selector The selector of the searched element.
         * @returns {*} The first element which is a child of the given element and matches the givens selector.
         */
        function findFirst(el, selector) {
          return _frameworkProto.find.call(el, selector).eq(0);
        }

        //==== API ====//

        /**
         * Puts the instance to sleep. It wont respond to any changes in the DOM and won't update. Scrollbar Interactivity is also disabled as well as the resize handle.
         * This behavior can be reset by calling the update method.
         */
        _base.sleep = function () {
          _sleeping = true;
        };

        /**
         * Updates the plugin and DOM to the current options.
         * This method should only be called if a update is 100% required.
         * @param force True if every property shall be updated and the cache shall be ignored.
         * !INTERNAL USAGE! : force can be a string "auto", "sync" or "zoom" too
         * if "auto" then before a real update the content size and host element attributes gets checked, and if they changed only then the update method will be called.
         * if "sync" then the async update process (MutationObserver or UpdateLoop) gets synchronized and a corresponding update takes place if one was needed due to pending changes.
         * if "zoom" then a update takes place where it's assumed that content and host size changed
         * @returns {boolean|undefined}
         * If force is "sync" then a boolean is returned which indicates whether a update was needed due to pending changes.
         * If force is "auto" then a boolean is returned whether a update was needed due to attribute or size changes.
         * undefined otherwise.
         */
        _base.update = function (force) {
          if (_destroyed) return;

          var attrsChanged;
          var contentSizeC;
          var isString = type(force) == TYPES.s;
          var doUpdateAuto;
          var mutHost;
          var mutContent;

          if (isString) {
            if (force === _strAuto) {
              attrsChanged = meaningfulAttrsChanged();
              contentSizeC = updateAutoContentSizeChanged();
              doUpdateAuto = attrsChanged || contentSizeC;
              if (doUpdateAuto) {
                update({
                  _contentSizeChanged: contentSizeC,
                  _changedOptions: _initialized
                    ? undefined
                    : _currentPreparedOptions,
                });
              }
            } else if (force === _strSync) {
              if (_mutationObserversConnected) {
                mutHost = _mutationObserverHostCallback(
                  _mutationObserverHost.takeRecords()
                );
                mutContent = _mutationObserverContentCallback(
                  _mutationObserverContent.takeRecords()
                );
              } else {
                mutHost = _base.update(_strAuto);
              }
            } else if (force === "zoom") {
              update({
                _hostSizeChanged: true,
                _contentSizeChanged: true,
              });
            }
          } else {
            force = _sleeping || force;
            _sleeping = false;
            if (!_base.update(_strSync) || force) update({ _force: force });
          }

          updateElementsOnLoad();

          return doUpdateAuto || mutHost || mutContent;
        };

        /**
                 Gets or sets the current options. The update method will be called automatically if new options were set.
                 * @param newOptions If new options are given, then the new options will be set, if new options aren't given (undefined or a not a plain object) then the current options will be returned.
                 * @param value If new options is a property path string, then this value will be used to set the option to which the property path string leads.
                 * @returns {*}
                 */
        _base.options = function (newOptions, value) {
          var option = {};
          var changedOps;

          //return current options if newOptions are undefined or empty
          if (
            FRAMEWORK.isEmptyObject(newOptions) ||
            !FRAMEWORK.isPlainObject(newOptions)
          ) {
            if (type(newOptions) == TYPES.s) {
              if (arguments.length > 1) {
                setObjectPropVal(option, newOptions, value);
                changedOps = setOptions(option);
              } else return getObjectPropVal(_currentOptions, newOptions);
            } else return _currentOptions;
          } else {
            changedOps = setOptions(newOptions);
          }

          if (!FRAMEWORK.isEmptyObject(changedOps)) {
            update({ _changedOptions: changedOps });
          }
        };

        /**
         * Restore the DOM, disconnects all observers, remove all resize observers and put the instance to sleep.
         */
        _base.destroy = function () {
          if (_destroyed) return;

          //remove this instance from auto update loop
          autoUpdateLoop.remove(_base);

          //disconnect all mutation observers
          disconnectMutationObservers();

          //remove all resize observers
          setupResizeObserver(_sizeObserverElement);
          setupResizeObserver(_sizeAutoObserverElement);

          //remove all extensions
          for (var extName in _extensions) _base.removeExt(extName);

          //remove all 'destroy' events
          while (_destroyEvents[LEXICON.l] > 0) _destroyEvents.pop()();

          //remove all events from host element
          setupHostMouseTouchEvents(true);

          //remove all helper / detection elements
          if (_contentGlueElement) remove(_contentGlueElement);
          if (_contentArrangeElement) remove(_contentArrangeElement);
          if (_sizeAutoObserverAdded) remove(_sizeAutoObserverElement);

          //remove all generated DOM
          setupScrollbarsDOM(true);
          setupScrollbarCornerDOM(true);
          setupStructureDOM(true);

          //remove all generated image load events
          for (var i = 0; i < _updateOnLoadElms[LEXICON.l]; i++)
            FRAMEWORK(_updateOnLoadElms[i]).off(
              _updateOnLoadEventName,
              updateOnLoadCallback
            );
          _updateOnLoadElms = undefined;

          _destroyed = true;
          _sleeping = true;

          //remove this instance from the instances list
          INSTANCES(pluginTargetElement, 0);
          dispatchCallback("onDestroyed");

          //remove all properties and methods
          //for (var property in _base)
          //    delete _base[property];
          //_base = undefined;
        };

        /**
         * Scrolls to a given position or element.
         * @param coordinates
         * 1. Can be "coordinates" which looks like:
         *    { x : ?, y : ? } OR          Object with x and y properties
         *    { left : ?, top : ? } OR     Object with left and top properties
         *    { l : ?, t : ? } OR          Object with l and t properties
         *    [ ?, ? ] OR                  Array where the first two element are the coordinates (first is x, second is y)
         *    ?                            A single value which stays for both axis
         *    A value can be a number, a string or a calculation.
         *
         *    Operators:
         *    [NONE]  The current scroll will be overwritten by the value.
         *    '+='    The value will be added to the current scroll offset
         *    '-='    The value will be subtracted from the current scroll offset
         *    '*='    The current scroll wil be multiplicated by the value.
         *    '/='    The current scroll wil be divided by the value.
         *
         *    Units:
         *    [NONE]  The value is the final scroll amount.                   final = (value * 1)
         *    'px'    Same as none
         *    '%'     The value is dependent on the current scroll value.     final = ((currentScrollValue / 100) * value)
         *    'vw'    The value is multiplicated by the viewport width.       final = (value * viewportWidth)
         *    'vh'    The value is multiplicated by the viewport height.      final = (value * viewportHeight)
         *
         *    example final values:
         *    200, '200px', '50%', '1vw', '1vh', '+=200', '/=1vw', '*=2px', '-=5vh', '+=33%', '+= 50% - 2px', '-= 1vw - 50%'
         *
         * 2. Can be a HTML or jQuery element:
         *    The final scroll offset is the offset (without margin) of the given HTML / jQuery element.
         *
         * 3. Can be a object with a HTML or jQuery element with additional settings:
         *    {
         *      el : [HTMLElement, jQuery element],             MUST be specified, else this object isn't valid.
         *      scroll : [string, array, object],               Default value is 'always'.
         *      block : [string, array, object],                Default value is 'begin'.
         *      margin : [number, boolean, array, object]       Default value is false.
         *    }
         *
         *    Possible scroll settings are:
         *    'always'      Scrolls always.
         *    'ifneeded'    Scrolls only if the element isnt fully in view.
         *    'never'       Scrolls never.
         *
         *    Possible block settings are:
         *    'begin'   Both axis shall be docked to the "begin" edge. - The element will be docked to the top and left edge of the viewport.
         *    'end'     Both axis shall be docked to the "end" edge. - The element will be docked to the bottom and right edge of the viewport. (If direction is RTL to the bottom and left edge.)
         *    'center'  Both axis shall be docked to "center". - The element will be centered in the viewport.
         *    'nearest' The element will be docked to the nearest edge(s).
         *
         *    Possible margin settings are: -- The actual margin of the element wont be affect, this option affects only the final scroll offset.
         *    [BOOLEAN]                                         If true the css margin of the element will be used, if false no margin will be used.
         *    [NUMBER]                                          The margin will be used for all edges.
         *
         * @param duration The duration of the scroll animation, OR a jQuery animation configuration object.
         * @param easing The animation easing.
         * @param complete The animation complete callback.
         * @returns {{
         *   position: {x: number, y: number},
         *   ratio: {x: number, y: number},
         *   max: {x: number, y: number},
         *   handleOffset: {x: number, y: number},
         *   handleLength: {x: number, y: number},
         *   handleLengthRatio: {x: number, y: number}, t
         *   rackLength: {x: number, y: number},
         *   isRTL: boolean,
         *   isRTLNormalized: boolean
         *  }}
         */
        _base.scroll = function (coordinates, duration, easing, complete) {
          if (arguments.length === 0 || coordinates === undefined) {
            var infoX = _scrollHorizontalInfo;
            var infoY = _scrollVerticalInfo;
            var normalizeInvert =
              _normalizeRTLCache && _isRTL && _rtlScrollBehavior.i;
            var normalizeNegate =
              _normalizeRTLCache && _isRTL && _rtlScrollBehavior.n;
            var scrollX = infoX._currentScroll;
            var scrollXRatio = infoX._currentScrollRatio;
            var maxScrollX = infoX._maxScroll;
            scrollXRatio = normalizeInvert ? 1 - scrollXRatio : scrollXRatio;
            scrollX = normalizeInvert ? maxScrollX - scrollX : scrollX;
            scrollX *= normalizeNegate ? -1 : 1;
            maxScrollX *= normalizeNegate ? -1 : 1;

            return {
              position: {
                x: scrollX,
                y: infoY._currentScroll,
              },
              ratio: {
                x: scrollXRatio,
                y: infoY._currentScrollRatio,
              },
              max: {
                x: maxScrollX,
                y: infoY._maxScroll,
              },
              handleOffset: {
                x: infoX._handleOffset,
                y: infoY._handleOffset,
              },
              handleLength: {
                x: infoX._handleLength,
                y: infoY._handleLength,
              },
              handleLengthRatio: {
                x: infoX._handleLengthRatio,
                y: infoY._handleLengthRatio,
              },
              trackLength: {
                x: infoX._trackLength,
                y: infoY._trackLength,
              },
              snappedHandleOffset: {
                x: infoX._snappedHandleOffset,
                y: infoY._snappedHandleOffset,
              },
              isRTL: _isRTL,
              isRTLNormalized: _normalizeRTLCache,
            };
          }

          _base.update(_strSync);

          var normalizeRTL = _normalizeRTLCache;
          var coordinatesXAxisProps = [_strX, _strLeft, "l"];
          var coordinatesYAxisProps = [_strY, _strTop, "t"];
          var coordinatesOperators = ["+=", "-=", "*=", "/="];
          var durationIsObject = type(duration) == TYPES.o;
          var completeCallback = durationIsObject
            ? duration.complete
            : complete;
          var i;
          var finalScroll = {};
          var specialEasing = {};
          var doScrollLeft;
          var doScrollTop;
          var animationOptions;
          var strEnd = "end";
          var strBegin = "begin";
          var strCenter = "center";
          var strNearest = "nearest";
          var strAlways = "always";
          var strNever = "never";
          var strIfNeeded = "ifneeded";
          var strLength = LEXICON.l;
          var settingsAxis;
          var settingsScroll;
          var settingsBlock;
          var settingsMargin;
          var finalElement;
          var elementObjSettingsAxisValues = [_strX, _strY, "xy", "yx"];
          var elementObjSettingsBlockValues = [
            strBegin,
            strEnd,
            strCenter,
            strNearest,
          ];
          var elementObjSettingsScrollValues = [
            strAlways,
            strNever,
            strIfNeeded,
          ];
          var coordinatesIsElementObj = coordinates[LEXICON.hOP]("el");
          var possibleElement = coordinatesIsElementObj
            ? coordinates.el
            : coordinates;
          var possibleElementIsJQuery =
            possibleElement instanceof FRAMEWORK || JQUERY
              ? possibleElement instanceof JQUERY
              : false;
          var possibleElementIsHTMLElement = possibleElementIsJQuery
            ? false
            : isHTMLElement(possibleElement);
          var updateScrollbarInfos = function () {
            if (doScrollLeft) refreshScrollbarHandleOffset(true);
            if (doScrollTop) refreshScrollbarHandleOffset(false);
          };
          var proxyCompleteCallback =
            type(completeCallback) != TYPES.f
              ? undefined
              : function () {
                  updateScrollbarInfos();
                  completeCallback();
                };
          function checkSettingsStringValue(currValue, allowedValues) {
            for (i = 0; i < allowedValues[strLength]; i++) {
              if (currValue === allowedValues[i]) return true;
            }
            return false;
          }
          function getRawScroll(isX, coordinates) {
            var coordinateProps = isX
              ? coordinatesXAxisProps
              : coordinatesYAxisProps;
            coordinates =
              type(coordinates) == TYPES.s || type(coordinates) == TYPES.n
                ? [coordinates, coordinates]
                : coordinates;

            if (COMPATIBILITY.isA(coordinates))
              return isX ? coordinates[0] : coordinates[1];
            else if (type(coordinates) == TYPES.o) {
              //decides RTL normalization "hack" with .n
              //normalizeRTL = type(coordinates.n) == TYPES.b ? coordinates.n : normalizeRTL;
              for (i = 0; i < coordinateProps[strLength]; i++)
                if (coordinateProps[i] in coordinates)
                  return coordinates[coordinateProps[i]];
            }
          }
          function getFinalScroll(isX, rawScroll) {
            var isString = type(rawScroll) == TYPES.s;
            var operator;
            var amount;
            var scrollInfo = isX ? _scrollHorizontalInfo : _scrollVerticalInfo;
            var currScroll = scrollInfo._currentScroll;
            var maxScroll = scrollInfo._maxScroll;
            var mult = " * ";
            var finalValue;
            var isRTLisX = _isRTL && isX;
            var normalizeShortcuts =
              isRTLisX && _rtlScrollBehavior.n && !normalizeRTL;
            var strReplace = "replace";
            var evalFunc = eval;
            var possibleOperator;
            if (isString) {
              //check operator
              if (rawScroll[strLength] > 2) {
                possibleOperator = rawScroll.substr(0, 2);
                if (inArray(possibleOperator, coordinatesOperators) > -1)
                  operator = possibleOperator;
              }

              //calculate units and shortcuts
              rawScroll = operator ? rawScroll.substr(2) : rawScroll;
              rawScroll = rawScroll[strReplace](/min/g, 0) //'min' = 0%
                [strReplace](/</g, 0) //'<'   = 0%
                [strReplace](
                  /max/g,
                  (normalizeShortcuts ? "-" : _strEmpty) + _strHundredPercent
                ) //'max' = 100%
                [strReplace](
                  />/g,
                  (normalizeShortcuts ? "-" : _strEmpty) + _strHundredPercent
                ) //'>'   = 100%
                [strReplace](/px/g, _strEmpty)
                [strReplace](
                  /%/g,
                  mult +
                    (maxScroll * (isRTLisX && _rtlScrollBehavior.n ? -1 : 1)) /
                      100.0
                )
                [strReplace](/vw/g, mult + _viewportSize.w)
                [strReplace](/vh/g, mult + _viewportSize.h);
              amount = parseToZeroOrNumber(
                isNaN(rawScroll)
                  ? parseToZeroOrNumber(evalFunc(rawScroll), true).toFixed()
                  : rawScroll
              );
            } else {
              amount = rawScroll;
            }

            if (
              amount !== undefined &&
              !isNaN(amount) &&
              type(amount) == TYPES.n
            ) {
              var normalizeIsRTLisX = normalizeRTL && isRTLisX;
              var operatorCurrScroll =
                currScroll *
                (normalizeIsRTLisX && _rtlScrollBehavior.n ? -1 : 1);
              var invert = normalizeIsRTLisX && _rtlScrollBehavior.i;
              var negate = normalizeIsRTLisX && _rtlScrollBehavior.n;
              operatorCurrScroll = invert
                ? maxScroll - operatorCurrScroll
                : operatorCurrScroll;
              switch (operator) {
                case "+=":
                  finalValue = operatorCurrScroll + amount;
                  break;
                case "-=":
                  finalValue = operatorCurrScroll - amount;
                  break;
                case "*=":
                  finalValue = operatorCurrScroll * amount;
                  break;
                case "/=":
                  finalValue = operatorCurrScroll / amount;
                  break;
                default:
                  finalValue = amount;
                  break;
              }
              finalValue = invert ? maxScroll - finalValue : finalValue;
              finalValue *= negate ? -1 : 1;
              finalValue =
                isRTLisX && _rtlScrollBehavior.n
                  ? MATH.min(0, MATH.max(maxScroll, finalValue))
                  : MATH.max(0, MATH.min(maxScroll, finalValue));
            }
            return finalValue === currScroll ? undefined : finalValue;
          }
          function getPerAxisValue(
            value,
            valueInternalType,
            defaultValue,
            allowedValues
          ) {
            var resultDefault = [defaultValue, defaultValue];
            var valueType = type(value);
            var valueArrLength;
            var valueArrItem;

            //value can be [ string, or array of two strings ]
            if (valueType == valueInternalType) {
              value = [value, value];
            } else if (valueType == TYPES.a) {
              valueArrLength = value[strLength];
              if (valueArrLength > 2 || valueArrLength < 1)
                value = resultDefault;
              else {
                if (valueArrLength === 1) value[1] = defaultValue;
                for (i = 0; i < valueArrLength; i++) {
                  valueArrItem = value[i];
                  if (
                    type(valueArrItem) != valueInternalType ||
                    !checkSettingsStringValue(valueArrItem, allowedValues)
                  ) {
                    value = resultDefault;
                    break;
                  }
                }
              }
            } else if (valueType == TYPES.o)
              value = [
                value[_strX] || defaultValue,
                value[_strY] || defaultValue,
              ];
            else value = resultDefault;
            return { x: value[0], y: value[1] };
          }
          function generateMargin(marginTopRightBottomLeftArray) {
            var result = [];
            var currValue;
            var currValueType;
            var valueDirections = [_strTop, _strRight, _strBottom, _strLeft];
            for (i = 0; i < marginTopRightBottomLeftArray[strLength]; i++) {
              if (i === valueDirections[strLength]) break;
              currValue = marginTopRightBottomLeftArray[i];
              currValueType = type(currValue);
              if (currValueType == TYPES.b)
                result.push(
                  currValue
                    ? parseToZeroOrNumber(
                        finalElement.css(_strMarginMinus + valueDirections[i])
                      )
                    : 0
                );
              else result.push(currValueType == TYPES.n ? currValue : 0);
            }
            return result;
          }

          if (possibleElementIsJQuery || possibleElementIsHTMLElement) {
            //get settings
            var margin = coordinatesIsElementObj ? coordinates.margin : 0;
            var axis = coordinatesIsElementObj ? coordinates.axis : 0;
            var scroll = coordinatesIsElementObj ? coordinates.scroll : 0;
            var block = coordinatesIsElementObj ? coordinates.block : 0;
            var marginDefault = [0, 0, 0, 0];
            var marginType = type(margin);
            var marginLength;
            finalElement = possibleElementIsJQuery
              ? possibleElement
              : FRAMEWORK(possibleElement);

            if (finalElement[strLength] > 0) {
              //margin can be [ boolean, number, array of 2, array of 4, object ]
              if (marginType == TYPES.n || marginType == TYPES.b)
                margin = generateMargin([margin, margin, margin, margin]);
              else if (marginType == TYPES.a) {
                marginLength = margin[strLength];
                if (marginLength === 2)
                  margin = generateMargin([
                    margin[0],
                    margin[1],
                    margin[0],
                    margin[1],
                  ]);
                else if (marginLength >= 4) margin = generateMargin(margin);
                else margin = marginDefault;
              } else if (marginType == TYPES.o)
                margin = generateMargin([
                  margin[_strTop],
                  margin[_strRight],
                  margin[_strBottom],
                  margin[_strLeft],
                ]);
              else margin = marginDefault;

              //block = type(block) === TYPES.b ? block ? [ strNearest, strBegin ] : [ strNearest, strEnd ] : block;
              settingsAxis = checkSettingsStringValue(
                axis,
                elementObjSettingsAxisValues
              )
                ? axis
                : "xy";
              settingsScroll = getPerAxisValue(
                scroll,
                TYPES.s,
                strAlways,
                elementObjSettingsScrollValues
              );
              settingsBlock = getPerAxisValue(
                block,
                TYPES.s,
                strBegin,
                elementObjSettingsBlockValues
              );
              settingsMargin = margin;

              var viewportScroll = {
                l: _scrollHorizontalInfo._currentScroll,
                t: _scrollVerticalInfo._currentScroll,
              };
              // use padding element instead of viewport element because padding element has never padding, margin or position applied.
              var viewportOffset = _paddingElement.offset();

              //get coordinates
              var elementOffset = finalElement.offset();
              var doNotScroll = {
                x: settingsScroll.x == strNever || settingsAxis == _strY,
                y: settingsScroll.y == strNever || settingsAxis == _strX,
              };
              elementOffset[_strTop] -= settingsMargin[0];
              elementOffset[_strLeft] -= settingsMargin[3];
              var elementScrollCoordinates = {
                x: MATH.round(
                  elementOffset[_strLeft] -
                    viewportOffset[_strLeft] +
                    viewportScroll.l
                ),
                y: MATH.round(
                  elementOffset[_strTop] -
                    viewportOffset[_strTop] +
                    viewportScroll.t
                ),
              };
              if (_isRTL) {
                if (!_rtlScrollBehavior.n && !_rtlScrollBehavior.i)
                  elementScrollCoordinates.x = MATH.round(
                    viewportOffset[_strLeft] -
                      elementOffset[_strLeft] +
                      viewportScroll.l
                  );
                if (_rtlScrollBehavior.n && normalizeRTL)
                  elementScrollCoordinates.x *= -1;
                if (_rtlScrollBehavior.i && normalizeRTL)
                  elementScrollCoordinates.x = MATH.round(
                    viewportOffset[_strLeft] -
                      elementOffset[_strLeft] +
                      (_scrollHorizontalInfo._maxScroll - viewportScroll.l)
                  );
              }

              //measuring is required
              if (
                settingsBlock.x != strBegin ||
                settingsBlock.y != strBegin ||
                settingsScroll.x == strIfNeeded ||
                settingsScroll.y == strIfNeeded ||
                _isRTL
              ) {
                var measuringElm = finalElement[0];
                var rawElementSize = _supportTransform
                  ? measuringElm[LEXICON.bCR]()
                  : {
                      width: measuringElm[LEXICON.oW],
                      height: measuringElm[LEXICON.oH],
                    };
                var elementSize = {
                  w:
                    rawElementSize[_strWidth] +
                    settingsMargin[3] +
                    settingsMargin[1],
                  h:
                    rawElementSize[_strHeight] +
                    settingsMargin[0] +
                    settingsMargin[2],
                };
                var finalizeBlock = function (isX) {
                  var vars = getScrollbarVars(isX);
                  var wh = vars._w_h;
                  var lt = vars._left_top;
                  var xy = vars._x_y;
                  var blockIsEnd =
                    settingsBlock[xy] ==
                    (isX ? (_isRTL ? strBegin : strEnd) : strEnd);
                  var blockIsCenter = settingsBlock[xy] == strCenter;
                  var blockIsNearest = settingsBlock[xy] == strNearest;
                  var scrollNever = settingsScroll[xy] == strNever;
                  var scrollIfNeeded = settingsScroll[xy] == strIfNeeded;
                  var vpSize = _viewportSize[wh];
                  var vpOffset = viewportOffset[lt];
                  var elSize = elementSize[wh];
                  var elOffset = elementOffset[lt];
                  var divide = blockIsCenter ? 2 : 1;
                  var elementCenterOffset = elOffset + elSize / 2;
                  var viewportCenterOffset = vpOffset + vpSize / 2;
                  var isInView =
                    elSize <= vpSize &&
                    elOffset >= vpOffset &&
                    elOffset + elSize <= vpOffset + vpSize;

                  if (scrollNever) doNotScroll[xy] = true;
                  else if (!doNotScroll[xy]) {
                    if (blockIsNearest || scrollIfNeeded) {
                      doNotScroll[xy] = scrollIfNeeded ? isInView : false;
                      blockIsEnd =
                        elSize < vpSize
                          ? elementCenterOffset > viewportCenterOffset
                          : elementCenterOffset < viewportCenterOffset;
                    }
                    elementScrollCoordinates[xy] -=
                      blockIsEnd || blockIsCenter
                        ? (vpSize / divide - elSize / divide) *
                          (isX && _isRTL && normalizeRTL ? -1 : 1)
                        : 0;
                  }
                };
                finalizeBlock(true);
                finalizeBlock(false);
              }

              if (doNotScroll.y) delete elementScrollCoordinates.y;
              if (doNotScroll.x) delete elementScrollCoordinates.x;

              coordinates = elementScrollCoordinates;
            }
          }

          finalScroll[_strScrollLeft] = getFinalScroll(
            true,
            getRawScroll(true, coordinates)
          );
          finalScroll[_strScrollTop] = getFinalScroll(
            false,
            getRawScroll(false, coordinates)
          );
          doScrollLeft = finalScroll[_strScrollLeft] !== undefined;
          doScrollTop = finalScroll[_strScrollTop] !== undefined;

          if (
            (doScrollLeft || doScrollTop) &&
            (duration > 0 || durationIsObject)
          ) {
            if (durationIsObject) {
              duration.complete = proxyCompleteCallback;
              _viewportElement.animate(finalScroll, duration);
            } else {
              animationOptions = {
                duration: duration,
                complete: proxyCompleteCallback,
              };
              if (
                COMPATIBILITY.isA(easing) ||
                FRAMEWORK.isPlainObject(easing)
              ) {
                specialEasing[_strScrollLeft] = easing[0] || easing.x;
                specialEasing[_strScrollTop] = easing[1] || easing.y;
                animationOptions.specialEasing = specialEasing;
              } else {
                animationOptions.easing = easing;
              }
              _viewportElement.animate(finalScroll, animationOptions);
            }
          } else {
            if (doScrollLeft)
              _viewportElement[_strScrollLeft](finalScroll[_strScrollLeft]);
            if (doScrollTop)
              _viewportElement[_strScrollTop](finalScroll[_strScrollTop]);
            updateScrollbarInfos();
          }
        };

        /**
         * Stops all scroll animations.
         * @returns {*} The current OverlayScrollbars instance (for chaining).
         */
        _base.scrollStop = function (param1, param2, param3) {
          _viewportElement.stop(param1, param2, param3);
          return _base;
        };

        /**
         * Returns all relevant elements.
         * @param elementName The name of the element which shall be returned.
         * @returns {{target: *, host: *, padding: *, viewport: *, content: *, scrollbarHorizontal: {scrollbar: *, track: *, handle: *}, scrollbarVertical: {scrollbar: *, track: *, handle: *}, scrollbarCorner: *} | *}
         */
        _base.getElements = function (elementName) {
          var obj = {
            target: _targetElementNative,
            host: _hostElementNative,
            padding: _paddingElementNative,
            viewport: _viewportElementNative,
            content: _contentElementNative,
            scrollbarHorizontal: {
              scrollbar: _scrollbarHorizontalElement[0],
              track: _scrollbarHorizontalTrackElement[0],
              handle: _scrollbarHorizontalHandleElement[0],
            },
            scrollbarVertical: {
              scrollbar: _scrollbarVerticalElement[0],
              track: _scrollbarVerticalTrackElement[0],
              handle: _scrollbarVerticalHandleElement[0],
            },
            scrollbarCorner: _scrollbarCornerElement[0],
          };
          return type(elementName) == TYPES.s
            ? getObjectPropVal(obj, elementName)
            : obj;
        };

        /**
         * Returns a object which describes the current state of this instance.
         * @param stateProperty A specific property from the state object which shall be returned.
         * @returns {{widthAuto, heightAuto, overflowAmount, hideOverflow, hasOverflow, contentScrollSize, viewportSize, hostSize, autoUpdate} | *}
         */
        _base.getState = function (stateProperty) {
          function prepare(obj) {
            if (!FRAMEWORK.isPlainObject(obj)) return obj;
            var extended = extendDeep({}, obj);
            var changePropertyName = function (from, to) {
              if (extended[LEXICON.hOP](from)) {
                extended[to] = extended[from];
                delete extended[from];
              }
            };
            changePropertyName("w", _strWidth); //change w to width
            changePropertyName("h", _strHeight); //change h to height
            delete extended.c; //delete c (the 'changed' prop)
            return extended;
          }
          var obj = {
            destroyed: !!prepare(_destroyed),
            sleeping: !!prepare(_sleeping),
            autoUpdate: prepare(!_mutationObserversConnected),
            widthAuto: prepare(_widthAutoCache),
            heightAuto: prepare(_heightAutoCache),
            padding: prepare(_cssPaddingCache),
            overflowAmount: prepare(_overflowAmountCache),
            hideOverflow: prepare(_hideOverflowCache),
            hasOverflow: prepare(_hasOverflowCache),
            contentScrollSize: prepare(_contentScrollSizeCache),
            viewportSize: prepare(_viewportSize),
            hostSize: prepare(_hostSizeCache),
            documentMixed: prepare(_documentMixed),
          };
          return type(stateProperty) == TYPES.s
            ? getObjectPropVal(obj, stateProperty)
            : obj;
        };

        /**
         * Gets all or specific extension instance.
         * @param extName The name of the extension from which the instance shall be got.
         * @returns {{}} The instance of the extension with the given name or undefined if the instance couldn't be found.
         */
        _base.ext = function (extName) {
          var result;
          var privateMethods = _extensionsPrivateMethods.split(" ");
          var i = 0;
          if (type(extName) == TYPES.s) {
            if (_extensions[LEXICON.hOP](extName)) {
              result = extendDeep({}, _extensions[extName]);
              for (; i < privateMethods.length; i++)
                delete result[privateMethods[i]];
            }
          } else {
            result = {};
            for (i in _extensions) result[i] = extendDeep({}, _base.ext(i));
          }
          return result;
        };

        /**
         * Adds a extension to this instance.
         * @param extName The name of the extension which shall be added.
         * @param extensionOptions The extension options which shall be used.
         * @returns {{}} The instance of the added extension or undefined if the extension couldn't be added properly.
         */
        _base.addExt = function (extName, extensionOptions) {
          var registeredExtensionObj = _plugin.extension(extName);
          var instance;
          var instanceAdded;
          var instanceContract;
          var contractResult;
          var contractFulfilled = true;
          if (registeredExtensionObj) {
            if (!_extensions[LEXICON.hOP](extName)) {
              instance = registeredExtensionObj.extensionFactory.call(
                _base,
                extendDeep({}, registeredExtensionObj.defaultOptions),
                FRAMEWORK,
                COMPATIBILITY
              );

              if (instance) {
                instanceContract = instance.contract;
                if (type(instanceContract) == TYPES.f) {
                  contractResult = instanceContract(window);
                  contractFulfilled =
                    type(contractResult) == TYPES.b
                      ? contractResult
                      : contractFulfilled;
                }
                if (contractFulfilled) {
                  _extensions[extName] = instance;
                  instanceAdded = instance.added;
                  if (type(instanceAdded) == TYPES.f)
                    instanceAdded(extensionOptions);

                  return _base.ext(extName);
                }
              }
            } else return _base.ext(extName);
          } else
            console.warn(
              'A extension with the name "' + extName + "\" isn't registered."
            );
        };

        /**
         * Removes a extension from this instance.
         * @param extName The name of the extension which shall be removed.
         * @returns {boolean} True if the extension was removed, false otherwise e.g. if the extension wasn't added before.
         */
        _base.removeExt = function (extName) {
          var instance = _extensions[extName];
          var instanceRemoved;
          if (instance) {
            delete _extensions[extName];

            instanceRemoved = instance.removed;
            if (type(instanceRemoved) == TYPES.f) instanceRemoved();

            return true;
          }
          return false;
        };

        /**
         * Constructs the plugin.
         * @param targetElement The element to which the plugin shall be applied.
         * @param options The initial options of the plugin.
         * @param extensions The extension(s) which shall be added right after the initialization.
         * @returns {boolean} True if the plugin was successfully initialized, false otherwise.
         */
        function construct(targetElement, options, extensions) {
          _defaultOptions = globals.defaultOptions;
          _nativeScrollbarStyling = globals.nativeScrollbarStyling;
          _nativeScrollbarSize = extendDeep({}, globals.nativeScrollbarSize);
          _nativeScrollbarIsOverlaid = extendDeep(
            {},
            globals.nativeScrollbarIsOverlaid
          );
          _overlayScrollbarDummySize = extendDeep(
            {},
            globals.overlayScrollbarDummySize
          );
          _rtlScrollBehavior = extendDeep({}, globals.rtlScrollBehavior);

          //parse & set options but don't update
          setOptions(extendDeep({}, _defaultOptions, options));

          _cssCalc = globals.cssCalc;
          _msieVersion = globals.msie;
          _autoUpdateRecommended = globals.autoUpdateRecommended;
          _supportTransition = globals.supportTransition;
          _supportTransform = globals.supportTransform;
          _supportPassiveEvents = globals.supportPassiveEvents;
          _supportResizeObserver = globals.supportResizeObserver;
          _supportMutationObserver = globals.supportMutationObserver;
          _restrictedMeasuring = globals.restrictedMeasuring;
          _documentElement = FRAMEWORK(targetElement.ownerDocument);
          _documentElementNative = _documentElement[0];
          _windowElement = FRAMEWORK(
            _documentElementNative.defaultView ||
              _documentElementNative.parentWindow
          );
          _windowElementNative = _windowElement[0];
          _htmlElement = findFirst(_documentElement, "html");
          _bodyElement = findFirst(_htmlElement, "body");
          _targetElement = FRAMEWORK(targetElement);
          _targetElementNative = _targetElement[0];
          _isTextarea = _targetElement.is("textarea");
          _isBody = _targetElement.is("body");
          _documentMixed = _documentElementNative !== document;

          /* On a div Element The if checks only whether:
           * - the targetElement has the class "os-host"
           * - the targetElement has a a child with the class "os-padding"
           *
           * If that's the case, its assumed the DOM has already the following structure:
           * (The ".os-host" element is the targetElement)
           *
           *  <div class="os-host">
           *      <div class="os-resize-observer-host"></div>
           *      <div class="os-padding">
           *          <div class="os-viewport">
           *              <div class="os-content"></div>
           *          </div>
           *      </div>
           *      <div class="os-scrollbar os-scrollbar-horizontal ">
           *          <div class="os-scrollbar-track">
           *              <div class="os-scrollbar-handle"></div>
           *          </div>
           *      </div>
           *      <div class="os-scrollbar os-scrollbar-vertical">
           *          <div class="os-scrollbar-track">
           *              <div class="os-scrollbar-handle"></div>
           *          </div>
           *      </div>
           *      <div class="os-scrollbar-corner"></div>
           *  </div>
           *
           * =====================================================================================
           *
           * On a Textarea Element The if checks only whether:
           * - the targetElement has the class "os-textarea"
           * - the targetElement is inside a element with the class "os-content"
           *
           * If that's the case, its assumed the DOM has already the following structure:
           * (The ".os-textarea" (textarea) element is the targetElement)
           *
           *  <div class="os-host-textarea">
           *      <div class="os-resize-observer-host"></div>
           *      <div class="os-padding os-text-inherit">
           *          <div class="os-viewport os-text-inherit">
           *              <div class="os-content os-text-inherit">
           *                  <div class="os-textarea-cover"></div>
           *                  <textarea class="os-textarea os-text-inherit"></textarea>
           *              </div>
           *          </div>
           *      </div>
           *      <div class="os-scrollbar os-scrollbar-horizontal ">
           *          <div class="os-scrollbar-track">
           *              <div class="os-scrollbar-handle"></div>
           *          </div>
           *      </div>
           *      <div class="os-scrollbar os-scrollbar-vertical">
           *          <div class="os-scrollbar-track">
           *              <div class="os-scrollbar-handle"></div>
           *          </div>
           *      </div>
           *      <div class="os-scrollbar-corner"></div>
           *  </div>
           */
          _domExists = _isTextarea
            ? _targetElement.hasClass(_classNameTextareaElement) &&
              _targetElement.parent().hasClass(_classNameContentElement)
            : _targetElement.hasClass(_classNameHostElement) &&
              _targetElement.children(_strDot + _classNamePaddingElement)[
                LEXICON.l
              ];

          var initBodyScroll;
          var bodyMouseTouchDownListener;

          //check if the plugin hasn't to be initialized
          if (
            _nativeScrollbarIsOverlaid.x &&
            _nativeScrollbarIsOverlaid.y &&
            !_currentPreparedOptions.nativeScrollbarsOverlaid.initialize
          ) {
            _initialized = true; // workaround so the onInitializationWithdrawn callback below is fired
            dispatchCallback("onInitializationWithdrawn");
            if (_domExists) {
              setupStructureDOM(true);
              setupScrollbarsDOM(true);
              setupScrollbarCornerDOM(true);
            }

            _initialized = false;
            _destroyed = true;
            _sleeping = true;

            return _base;
          }

          if (_isBody) {
            initBodyScroll = {};
            initBodyScroll.l = MATH.max(
              _targetElement[_strScrollLeft](),
              _htmlElement[_strScrollLeft](),
              _windowElement[_strScrollLeft]()
            );
            initBodyScroll.t = MATH.max(
              _targetElement[_strScrollTop](),
              _htmlElement[_strScrollTop](),
              _windowElement[_strScrollTop]()
            );

            bodyMouseTouchDownListener = function () {
              _viewportElement.removeAttr(LEXICON.ti);
              setupResponsiveEventListener(
                _viewportElement,
                _strMouseTouchDownEvent,
                bodyMouseTouchDownListener,
                true,
                true
              );
            };
          }

          //build OverlayScrollbars DOM
          setupStructureDOM();
          setupScrollbarsDOM();
          setupScrollbarCornerDOM();

          //create OverlayScrollbars events
          setupStructureEvents();
          setupScrollbarEvents(true);
          setupScrollbarEvents(false);
          setupScrollbarCornerEvents();

          //create mutation observers
          createMutationObservers();

          //build resize observer for the host element
          setupResizeObserver(_sizeObserverElement, hostOnResized);

          if (_isBody) {
            //apply the body scroll to handle it right in the update method
            _viewportElement[_strScrollLeft](initBodyScroll.l)[_strScrollTop](
              initBodyScroll.t
            );

            //set the focus on the viewport element so you dont have to click on the page to use keyboard keys (up / down / space) for scrolling
            if (
              document.activeElement == targetElement &&
              _viewportElementNative.focus
            ) {
              //set a tabindex to make the viewportElement focusable
              _viewportElement.attr(LEXICON.ti, "-1");
              _viewportElementNative.focus();

              /* the tabindex has to be removed due to;
               * If you set the tabindex attribute on an <div>, then its child content cannot be scrolled with the arrow keys unless you set tabindex on the content, too
               * https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
               */
              setupResponsiveEventListener(
                _viewportElement,
                _strMouseTouchDownEvent,
                bodyMouseTouchDownListener,
                false,
                true
              );
            }
          }

          //update for the first time & initialize cache
          _base.update(_strAuto);

          //the plugin is initialized now!
          _initialized = true;
          dispatchCallback("onInitialized");

          //call all callbacks which would fire before the initialized was complete
          each(_callbacksInitQeueue, function (index, value) {
            dispatchCallback(value.n, value.a);
          });
          _callbacksInitQeueue = [];

          //add extensions
          if (type(extensions) == TYPES.s) extensions = [extensions];
          if (COMPATIBILITY.isA(extensions))
            each(extensions, function (index, value) {
              _base.addExt(value);
            });
          else if (FRAMEWORK.isPlainObject(extensions))
            each(extensions, function (key, value) {
              _base.addExt(key, value);
            });

          //add the transition class for transitions AFTER the first update & AFTER the applied extensions (for preventing unwanted transitions)
          setTimeout(function () {
            if (_supportTransition && !_destroyed)
              addClass(_hostElement, _classNameHostTransition);
          }, 333);

          return _base;
        }

        if (
          _plugin.valid(construct(pluginTargetElement, options, extensions))
        ) {
          INSTANCES(pluginTargetElement, _base);
        }

        return _base;
      }

      /**
       * Initializes a new OverlayScrollbarsInstance object or changes options if already initialized or returns the current instance.
       * @param pluginTargetElements The elements to which the Plugin shall be initialized.
       * @param options The custom options with which the plugin shall be initialized.
       * @param extensions The extension(s) which shall be added right after initialization.
       * @returns {*}
       */
      _plugin = window[PLUGINNAME] = function (
        pluginTargetElements,
        options,
        extensions
      ) {
        if (arguments[LEXICON.l] === 0) return this;

        var arr = [];
        var optsIsPlainObj = FRAMEWORK.isPlainObject(options);
        var inst;
        var result;

        //pluginTargetElements is null or undefined
        if (!pluginTargetElements)
          return optsIsPlainObj || !options ? result : arr;

        /*
                   pluginTargetElements will be converted to:
                   1. A jQueryElement Array
                   2. A HTMLElement Array
                   3. A Array with a single HTML Element
                   so pluginTargetElements is always a array.
                */
        pluginTargetElements =
          pluginTargetElements[LEXICON.l] != undefined
            ? pluginTargetElements
            : [pluginTargetElements[0] || pluginTargetElements];
        initOverlayScrollbarsStatics();

        if (pluginTargetElements[LEXICON.l] > 0) {
          if (optsIsPlainObj) {
            FRAMEWORK.each(pluginTargetElements, function (i, v) {
              inst = v;
              if (inst !== undefined)
                arr.push(
                  OverlayScrollbarsInstance(
                    inst,
                    options,
                    extensions,
                    _pluginsGlobals,
                    _pluginsAutoUpdateLoop
                  )
                );
            });
          } else {
            FRAMEWORK.each(pluginTargetElements, function (i, v) {
              inst = INSTANCES(v);
              if (
                (options === "!" && _plugin.valid(inst)) ||
                (COMPATIBILITY.type(options) == TYPES.f && options(v, inst))
              )
                arr.push(inst);
              else if (options === undefined) arr.push(inst);
            });
          }
          result = arr[LEXICON.l] === 1 ? arr[0] : arr;
        }
        return result;
      };

      /**
       * Returns a object which contains global information about the plugin and each instance of it.
       * The returned object is just a copy, that means that changes to the returned object won't have any effect to the original object.
       */
      _plugin.globals = function () {
        initOverlayScrollbarsStatics();
        var globals = FRAMEWORK.extend(true, {}, _pluginsGlobals);
        delete globals["msie"];
        return globals;
      };

      /**
       * Gets or Sets the default options for each new plugin initialization.
       * @param newDefaultOptions The object with which the default options shall be extended.
       */
      _plugin.defaultOptions = function (newDefaultOptions) {
        initOverlayScrollbarsStatics();
        var currDefaultOptions = _pluginsGlobals.defaultOptions;
        if (newDefaultOptions === undefined)
          return FRAMEWORK.extend(true, {}, currDefaultOptions);

        //set the new default options
        _pluginsGlobals.defaultOptions = FRAMEWORK.extend(
          true,
          {},
          currDefaultOptions,
          _pluginsOptions._validate(
            newDefaultOptions,
            _pluginsOptions._template,
            true,
            currDefaultOptions
          )._default
        );
      };

      /**
       * Checks whether the passed instance is a non-destroyed OverlayScrollbars instance.
       * @param osInstance The potential OverlayScrollbars instance which shall be checked.
       * @returns {boolean} True if the passed value is a non-destroyed OverlayScrollbars instance, false otherwise.
       */
      _plugin.valid = function (osInstance) {
        return (
          osInstance instanceof _plugin && !osInstance.getState().destroyed
        );
      };

      /**
       * Registers, Unregisters or returns a extension.
       * Register: Pass the name and the extension. (defaultOptions is optional)
       * Unregister: Pass the name and anything except a function as extension parameter.
       * Get extension: Pass the name of the extension which shall be got.
       * Get all extensions: Pass no arguments.
       * @param extensionName The name of the extension which shall be registered, unregistered or returned.
       * @param extension A function which generates the instance of the extension or anything other to remove a already registered extension.
       * @param defaultOptions The default options which shall be used for the registered extension.
       */
      _plugin.extension = function (extensionName, extension, defaultOptions) {
        var extNameTypeString = COMPATIBILITY.type(extensionName) == TYPES.s;
        var argLen = arguments[LEXICON.l];
        var i = 0;
        if (argLen < 1 || !extNameTypeString) {
          //return a copy of all extension objects
          return FRAMEWORK.extend(
            true,
            { length: _pluginsExtensions[LEXICON.l] },
            _pluginsExtensions
          );
        } else if (extNameTypeString) {
          if (COMPATIBILITY.type(extension) == TYPES.f) {
            //register extension
            _pluginsExtensions.push({
              name: extensionName,
              extensionFactory: extension,
              defaultOptions: defaultOptions,
            });
          } else {
            for (; i < _pluginsExtensions[LEXICON.l]; i++) {
              if (_pluginsExtensions[i].name === extensionName) {
                if (argLen > 1)
                  _pluginsExtensions.splice(i, 1); //remove extension
                else return FRAMEWORK.extend(true, {}, _pluginsExtensions[i]); //return extension with the given name
              }
            }
          }
        }
      };

      return _plugin;
    })();

    if (JQUERY && JQUERY.fn) {
      /**
       * The jQuery initialization interface.
       * @param options The initial options for the construction of the plugin. To initialize the plugin, this option has to be a object! If it isn't a object, the instance(s) are returned and the plugin wont be initialized.
       * @param extensions The extension(s) which shall be added right after initialization.
       * @returns {*} After initialization it returns the jQuery element array, else it returns the instance(s) of the elements which are selected.
       */
      JQUERY.fn.overlayScrollbars = function (options, extensions) {
        var _elements = this;
        if (JQUERY.isPlainObject(options)) {
          JQUERY.each(_elements, function () {
            PLUGIN(this, options, extensions);
          });
          return _elements;
        } else return PLUGIN(_elements, options);
      };
    }
    return PLUGIN;
  }
);
